使用乐观锁定时会出现间歇性错误。您需要添加代码来处理错误。
乐观的并发代码是这样说的,
“我很确定,甚至乐观,当我使用它时,没有其他人会触及这一行。因此,我不会费心‘悲观地’锁定这一行。这样我就不会妨碍其他只想阅读该行的人。不过,我并不愚蠢,所以我会在编写更改之前仔细检查,以确保在我不查看时没有其他人更新该行。如果他们做到了,我会提出一个错误而不是写我的更改。”
最终结果与悲观的方法基本相同;更新正在有序进行。除了现在,非写入者正在体验更好的读取性能。但是,如果很多东西都试图同时编辑相同的行,那么它们迟早会互相踩踏。如果这种情况发生得足够多,这对写入性能可能会非常不利,因为写入器必须不断重试直到成功。
你说,
“但是等等!SQL 更新是原子的!两个更新怎么可能‘互相踩踏’?”
我很高兴你问。随意花点时间阅读如果 2 个人或更多人同时更新记录会发生什么?
伟大的!你回来了。我们还有几件事要谈。
什么时候应该使用悲观锁而不是乐观并发? 好吧,名字说明了一切。当您乐观地认为通常不会发生并发更新时,请使用乐观并发。另一方面,如果您非常悲观地认为并发更新将一直发生,请使用悲观锁定,以便数据库服务器可以负责确保每个人都排队等候轮到他们。
如果出现乐观并发违规,我的代码应该怎么做? 基本上,它只需要重试直到通过。最简单的方法就是向用户显示一条消息,
“抱歉,这条记录已更改,您的更改无法保存,请重新加载页面重试。”
这让用户负责弄清楚应该如何解决冲突。不利的一面是,他将不得不重新输入他的更改。
您可能会喜欢在循环中自动重试直到没有收到该错误的代码,或者更喜欢检查已保存的其他更改并尝试智能地将更改合并在一起的代码。如果您实际上必须开始编写这样的代码,那么这可能是一个很好的迹象,表明您会更好地使用悲观锁定。
还有什么吗,哦,啰嗦的人?好吧,请记住,如果 Hibernate 或 NHibernate 抛出异常,您需要将 Session 和相关实体扔掉并获取新的实体。他们现在对数据库的外观有不一致的了解。您可能已经回滚了数据库事务,但这些内存中对象的状态并未回滚。如果这是一个 Web 应用程序,并且您正在执行 Session-per-request,并且您采用“告诉用户'对不起'”的方法,那么这将由用户再次单击“保存”按钮自然地处理。