我的应用程序中有一个场景,其中事务下的简单INSERT语句阻止了MySQL中不同行的DELETE语句,最终,DELETE语句会话超时,锁定超时错误。
我试图在下面的简单场景中解释这种情况。注意,在列上添加索引有点帮助,但是如果我在DELETE的WHERE子句中有2列,我仍然会看到锁等待超时。另请注意,在我开始事务之后,我需要做一些其他处理,因此事务需要几分钟才能提交或回滚。很难相信即使我删除了不同于我想要插入的记录,MySQL也会阻止它。
第1节: -
mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation, @@session.tx_isolation;
+-----------------------+----------------+------------------------+
| @@GLOBAL.tx_isolation | @@tx_isolation | @@session.tx_isolation |
+-----------------------+----------------+------------------------+
| READ-COMMITTED | READ-COMMITTED | READ-COMMITTED |
+-----------------------+----------------+------------------------+
1 row in set (0.02 sec)`
mysql> create table testtab(col1 int, col2 int);
Query OK, 0 rows affected (0.53 sec)
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO testtab values(1,1);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO testtab values(2,2);
Query OK, 1 row affected (0.00 sec)
第二节: -
mysql> SELECT @@GLOBAL.tx_isolation, @@tx_isolation, @@session.tx_isolation;
+-----------------------+----------------+------------------------+
| @@GLOBAL.tx_isolation | @@tx_isolation | @@session.tx_isolation |
+-----------------------+----------------+------------------------+
| READ-COMMITTED | READ-COMMITTED | READ-COMMITTED |
+-----------------------+----------------+------------------------+
1 row in set (0.00 sec)
mysql> DELETE FROM testtab where col1=3;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
有什么建议吗?
答案 0 :(得分:0)
这是InnoDB锁定工作的结果。引用manual,
锁定读取,UPDATE或DELETE通常会在处理SQL语句时扫描的每个索引记录上设置记录锁定。在语句中是否存在排除该行的WHERE条件并不重要。 InnoDB不记得确切的WHERE条件,但只知道扫描了哪些索引范围。
还值得注意的是
如果没有适合您的语句的索引,并且MySQL必须扫描整个表来处理该语句,则表的每一行都会被锁定,这反过来会阻止其他用户对表的所有插入。创建好的索引非常重要,这样您的查询就不会不必要地扫描很多行。
那么在你的情况下会发生的是两个insert
向表中添加两个锁定的行。由于没有合适的索引,delete
现在将扫描整个表。它必须锁定它遇到的每一行,但要做到这一点,它必须等待insert
创建的锁已被解除(这不会及时发生)。
在col1
上添加索引将解决此问题,因为delete
只需考虑(并锁定)可以使用索引找到的col1 = 3
行,因此它将与其他事务插入的行没有重叠。
如果您在查找"双列 - where-condition"的良好索引时遇到问题,您应该添加更多有关它的详细信息,可能有适合该情况的索引。
这当然不会阻止所有情况(例如,如果你真的想要修改同一行),所以通常最好保持你的交易尽可能短,准备等待那么久和/或准备好在必要时重复交易。
这不是InnoDB锁的完整描述,只是解释你情况的部分。