假设一个名为results
的MySQL表。 results
每天上午11点左右通过cron自动更新。但是,results
也会从面向用户的前端更新,大约上午11点,有很多用户执行的操作也会更新results
表。这意味着自动cron和用户更新通常会因“死锁”而失败。错误。
我们目前的解决方案:
我们已经实现了一个try / catch,它会在继续下一行之前重复尝试10次。我根本不喜欢这个解决方案,因为,它不是解决方案,只是一种解决方法,而且是一个错误的解决方案。如果死锁持续10次尝试,并且执行时间可能乘以10(在cron方面没有那么多问题,但绝对在用户端,那么仍然无法保证更新将完全正常工作) )。
我们即将实施的另一项变更是将cron移动到一天中的不同时间,以便在重型平台使用的同时不使自动更新运行。这应该可以缓解现在的大部分问题,但我仍然不喜欢它,因为它仍然只是一种解决方法。如果我们用户的使用模式发生变化且平台在此期间被大量使用,那么我们将再次遇到同样的问题。
是否有技术(代码)或架构(数据库设计)的解决方案可以帮助我减轻或消除这些死锁错误?
答案 0 :(得分:1)
当你有一个事务以非原子方式获取多行上的锁时,就会发生死锁,即更新行A,然后在一秒之后更新行B.
但是其他会话可能会在这些更新之间拆分并首先锁定行B,然后尝试锁定行A.它无法锁定行A,因为第一个会话已将其锁定。现在第一个会话不会放弃对A行的锁定,因为它在第二个会话锁定的B行等待。
解决方案:
所有会话必须以相同的顺序锁定行。因此,会话1或2将锁定行A,另一个将等待行A.只有在锁定行A之后,任何会话继续请求对行B的锁定。如果所有会话按升序锁定行,则它们将永远不会死锁(降序也适用,重点是所有会话都必须这样做)。
每个事务进行一次原子锁获取操作。那么你就无法获得这种交错效果。
使用悲观锁定。也就是说,锁定会话可能需要在其工作开始时在一个原子锁请求中更新的所有资源。这样做的一个例子是LOCK TABLES
声明。但这通常被认为是对并发访问表的阻碍。
您可能会喜欢我的演示文稿InnoDB Locking Explained with Stick Figures。关于死锁的部分从幻灯片68开始。