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