具有锁定模式S和X的InnoDB死锁

时间:2011-10-27 02:12:02

标签: mysql locking innodb deadlock

在我的应用程序中,我有两个不时发生的查询(来自不同的进程),这会导致死锁。

查询#1

UPDATE tblA, tblB SET tblA.varcharfield=tblB.varcharfield WHERE tblA.varcharfield IS NULL AND [a few other conditions];

查询#2

INSERT INTO tmp_tbl SELECT * FROM tblA WHERE [various conditions];

这两个查询都需要很长时间,因为这些表有数百万行。当查询#2正在运行时,似乎tblA被锁定在模式S中。查询#1似乎需要X锁定。由于这与S锁不兼容,因此查询#1等待最多30秒,此时我遇到了死锁:

  

序列化失败:1213尝试锁定时发现死锁;尝试重新启动交易

根据我所读的in the documentation,我想我有几个选择:

  1. 在tblA.varcharfield上设置索引。不幸的是,我认为这需要一个非常大的索引来存储varchar字段(512)。 (请参阅下面的编辑内容......这不起作用。)
  2. 使用SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 禁用锁定。我不明白这个含义,我担心数据损坏。我目前在我的应用程序中不使用显式事务,但我可能在将来的某个时候使用。
  3. 将耗时的查询拆分成小块,以便它们可以在MySQL中排队并运行,而不会达到30秒超时。这不会真正解决问题的核心,我担心当我的数据库服务器忙碌时会再次出现问题。
  4. 只是一遍又一遍地重试查询......不是我希望的选项。
  5. 我该怎么办?我应该考虑其他方法吗?


    编辑:我已尝试在varcharfield上设置索引,但该表仍处于锁定状态。我怀疑当UPDATE部分实际执行时会发生锁定。还有其他建议可以解决这个问题吗?

3 个答案:

答案 0 :(得分:1)

一个。如果我们假设索引varcharField占用了大量磁盘空间并且添加新列不会让您感到困难,我可以建议以下方法:

  1. 使用数据类型“tinyint”
  2. 创建新字段
  3. 索引它。
  4. 如果varcharField为空,则此字段将存储0,否则为1 -
  5. 重写第一个查询以依赖新字段进行更新。在这种情况下,它不会导致整个表锁定。
  6. 希望它有所帮助。

答案 1 :(得分:1)

您只能索引varchar列的一部分,它仍然可以工作,并且需要的空间更少。只需指定索引大小:

CREATE INDEX someindex ON sometable (varcharcolumn(32))

答案 2 :(得分:0)

我能够通过在两个查询周围添加显式LOCK TABLE语句来解决问题。事实证明这是一个更好的解决方案,因为每个查询都会影响很多记录,而且这两个记录都是后台进程。他们现在互相等待。

http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html

虽然这对我来说是一个不错的解决方案,但显然不是每个人的答案。使用WRITE锁定意味着您无法READ。只有READ锁定允许其他人READ