mysql - 如何解决经常死锁的 MySQL 表?

标签 mysql database innodb database-deadlocks

假设有一个名为 results 的 MySQL 表。 results 每天上午 11 点左右通过 cron 自动更新。但是,results 也是从面向用户的前端更新的,上午 11 点左右,有很多用户执行的操作也会更新 results 表。这意味着自动 cron 和用户更新经常因“死锁”错误而失败。


我们目前的解决方案:

我们已经实现了一个 try/catch,它将在移动到下一行之前重复尝试 10 次。我根本不喜欢这个解决方案,因为它不是一个解决方案,只是一个解决方法,而且是一个错误的解决方案。如果死锁在 10 次尝试中仍然存在,并且执行时间可能乘以 10(在 cron 端不是一个问题,但在用户端肯定是一个问题),仍然不能保证更新会起作用。

我们即将实现的另一项更改是将 cron 移至一天中的不同时间,以免在平台使用率过高的同时运行自动更新。这应该可以缓解现在的大部分问题,但我仍然不喜欢它,因为它仍然只是一种解决方法。如果我们用户的使用模式发生变化并且平台在此期间使用量很大,那么我们将再次遇到同样的问题。


是否有技术(代码)或架构(数据库设计)的解决方案可以帮助我减轻或完全消除这些死锁错误?

最佳答案

当您有一个事务以非原子方式获取多行的锁时,就会发生死锁,即更新行 A,然后在一瞬间更新行 B。

但是其他 session 有可能在这些更新之间拆分并首先锁定行 B,然后尝试锁定行 A。它无法锁定行 A,因为第一个 session 已将其锁定。现在第一个 session 不会放弃对 A 行的锁定,因为它正在等待第二个 session 已锁定的 B 行。

解决方案:

  • 所有 session 必须以相同的顺序锁定行。因此 session 1 或 session 2 将锁定行 A,另一个将等待行 A。只有在锁定行 A 之后,任何 session 才会继续请求锁定行 B。如果所有 session 都按升序锁定行,那么它们将永远不会死锁(降序也适用,关键是所有 session 都必须执行相同的操作)。

  • 对每个事务进行一次原子锁获取操作。那你就得不到这种交错的效果。

  • 使用悲观锁定。也就是说,在工作开始时锁定 session 可能需要在一个原子锁定请求中更新的所有资源。广泛执行此操作的一个示例是 LOCK TABLES 语句。但这通常被认为是对表的并发访问的障碍。

您可能会喜欢我的介绍 InnoDB Locking Explained with Stick Figures .关于死锁的部分从幻灯片 68 开始。

关于mysql - 如何解决经常死锁的 MySQL 表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47600226/

相关文章:

php - Redis sub/pub 和 php/nodejs

mysql - 如何在菜谱表中添加自引用以允许子菜谱?

r - 如何通过现有名称列表将矩阵行或列名称更改为新名称?

MySQL。多个信息排成一行

mysql - 为什么 MySQL(MyISAM 和 InnoDB)不使用我的索引?

php - 优化MySQL数据库查询

php - 想用一个MySQL表影响多个页面

mysql - Magento 让 MySQL 每天崩溃 1 到 2 次

mysql - 如何强制截断 MySQL 数据库中的所有表(都是 innodb)?

mysql - 从连接表中按 MAX(日期) 选择