【问题标题】:MySQL Deadlock ErrorMySQL 死锁错误
【发布时间】:2014-01-06 21:28:16
【问题描述】:

我的网络应用可能最多以 1-2 次/秒的速度运行以下查询,具体取决于用户流量:

UPDATE `click_rollups` 
   SET `clicks` = `clicks` + 1, `last_updated` = ? 
   WHERE `camp_id` = ? 
     AND `country` = ? 
     AND `clicks` < ? 
     AND `time_created` = ?

我们的日志显示有时会出现此错误:

SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction

但是,click_rollups 在此事务的写入上下文中仅使用一次,因此我无法想象会发生死锁的方式。它仅在应用程序的其他地方查询一次,仅使用SELECTs。

因此这是否意味着这两个单独事务(更新和仅选择)的死锁导致了问题,因为每个单独的事务仅使用此表一次(并且使用此表的查询不引用任何其他表)?或者是否存在行级锁定问题,这可能意味着其中一个事务可能与同一事务的其他事件发生死锁?

【问题讨论】:

  • 啊,我刚刚阅读了更多内容,发现由于 InnoDB 确实使用行级锁定,因此在插入单行时可能会发生死锁,并且应用程序应始终设计为在以下情况下重新发出事务它们由于死锁而回滚。
  • 我怀疑这个查询是在触发器中调用的,对吗?其次,一个表中有一个外键引用了另一个表,两个表都在同一个事务中更新,不是吗?
  • answer your own question 完全可以接受。我认为这个问题(有答案)可以作为未来访问者的一个很好的参考。
  • 没有触发器,也没有 FK。我会用我发现的内容发布答案

标签: mysql deadlock


【解决方案1】:

在阅读了更多内容后,我发现,由于 InnoDB 确实使用行级锁定,因此在插入或更新单行时可能会发生死锁,因为操作不是原子的。我跑了:

SHOW ENGINE INNODB STATUS

查找有关上次死锁的信息。我发现:

------------------------
LATEST DETECTED DEADLOCK
------------------------
140106 17:22:41
*** (1) TRANSACTION:
TRANSACTION 63EB5222A, ACTIVE 0 sec starting index read
mysql tables in use 3, locked 3
LOCK WAIT 9 lock struct(s), heap size 3112, 6 row lock(s), undo log entries 2
MySQL thread id 4304350, OS thread handle 0x7fd3b74d3700, query id 173460207 192.168.0.2 sharecash Updating
UPDATE `click_rollups` SET `clicks` = `clicks` + 1, `last_updated` = '1389046961' WHERE `camp_id` = '27739' AND `country` = 'US' AND `clicks` < '1000' AND `time_created` = '1389046866'
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 186 page no 407 n bits 1272 index `country` of table `sharecash`.`click_rollups` trx id 63EB5222A lock_mode X waiting
*** (2) TRANSACTION:
TRANSACTION 63EB52225, ACTIVE 0 sec fetching rows
mysql tables in use 3, locked 3
177 lock struct(s), heap size 31160, 17786 row lock(s), undo log entries 2
MySQL thread id 4304349, OS thread handle 0x7fd6961c8700, query id 173460194 192.168.0.1 sharecash Updating
UPDATE `click_rollups` SET `clicks` = `clicks` + 1, `last_updated` = '1389046961' WHERE `camp_id` = '30949' AND `country` = 'US' AND `clicks` < '1000' AND `time_created` = '1388964767'
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 186 page no 407 n bits 1272 index `country` of table `sharecash`.`click_rollups` trx id 63EB52225 lock_mode X
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 186 page no 512 n bits 384 index `PRIMARY` of table `sharecash`.`click_rollups` trx id 63EB52225 lock_mode X locks rec but not gap waiting
*** WE ROLL BACK TRANSACTION (1)

您可以看到导致死锁的两个查询实际上是完全相同的查询。它表明 WHERE 子句中的列也有不同的参数,因此被锁定的实际行是不同的,这对我来说似乎有点违反直觉——对不同行集的操作怎么会导致死锁?

答案似乎是死锁是由查询引擎锁定索引结构中的条目引起的。如果你看上面的输出,你可以看到一个事务在country索引中的某个页面的某个部分有锁,并且需要在主键索引的一部分上加锁,而另一个事务本质上是相反的情况。

在我们的应用程序的这一部分中,只有一行的点击次数会少于 1000 次,因此我相信通过修复该问题,死锁问题将被最小化,因为总体上会减少锁定。 MySQL 文档建议对您的应用程序进行编码,以便在由于死锁而回滚的情况下始终重新发出事务,这将防止此问题导致页面出错。但是,如果有人对如何真正避免这些死锁有任何其他想法,请再次将它们发布在 cmets 中!

编辑-

事务不需要使用country 索引,因为对于每个camp_id 值只有少数(通常只有1 个)不同的country 值,每个值只对应一行.我在查询中添加了索引提示,使其停止使用该索引,现在问题已得到修复,没有任何性能影响(可能是小幅提升)。

【讨论】:

    猜你喜欢
    • 2015-09-20
    • 1970-01-01
    • 2015-08-04
    • 2011-07-16
    • 1970-01-01
    • 2016-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多