MySQL 服务器版本 5.1.41,启用了 InnoDB 插件。我有以下三个发票表:invoices、invoice_components 和invoice_expenses。表发票有invoice_id 主键。发票_组件和发票_费用都链接到表发票,发票_id作为非唯一的外键(每张发票可以有多个组件和多个费用)。两个表都有该外键的 BTREE 索引。
我有以下交易:
交易1
START TRANSACTION;
SELECT * FROM invoices WHERE invoice_id = 18 FOR UPDATE;
SELECT * FROM invoice_components WHERE invoice = 18 FOR UPDATE;
SELECT * FROM invoice_expenses WHERE invoice = 18 FOR UPDATE;
第一个事务的一切正常,并且行被选择并锁定。
交易2
START TRANSACTION;
SELECT * FROM invoices WHERE invoice_id = 19 FOR UPDATE;
SELECT * FROM invoice_components WHERE invoice = 19 FOR UPDATE;
SELECT * FROM invoice_expenses WHERE invoice = 19 FOR UPDATE;
第二笔交易返回ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
对于第三个查询。
当我尝试选择...更新其他发票及其组成部分和费用时,也会发生同样的情况。似乎第一个事务已锁定invoice_expenses 表中的所有行。有什么想法为什么会发生这种情况吗?
附加信息
事务 2 在事务 1 的第三次查询之后开始。服务器上没有其他用户、连接或事务。
该问题发生在默认的 REPEATABLE READ 事务隔离级别中。它可以通过更改为 READ COMMITTED 级别来修复。这是一个解决方案,但它仍然无法解释为什么问题发生在invoice_expenses 而不是invoice_components。
我怀疑这与间隙锁 and 下一个钥匙锁以及行为上的差异可重复读取 :
摘录自 MySQL 文档:设置事务语法 http://dev.mysql.com/doc/refman/5.1/en/set-transaction.html
对于锁定读取(使用 FOR UPDATE 或 LOCK IN SHARE MODE 的 SELECT)、UPDATE 和 DELETE 语句,锁定取决于该语句是使用具有唯一搜索条件的唯一索引,还是使用范围类型搜索条件。对于具有唯一搜索条件的唯一索引,InnoDB 仅锁定找到的索引记录,而不锁定其之前的间隙。对于其他搜索条件,InnoDB 锁定扫描的索引范围,使用间隙锁或下一个键(间隙加索引记录)锁阻止其他会话插入该范围所覆盖的间隙。
and 已提交读 :
注意:在MySQL 5.1中,如果使用READ COMMITTED隔离级别或者启用了innodb_locks_unsafe_for_binlog系统变量,InnoDB 没有间隙锁定外键约束检查和重复键检查除外。还,释放不匹配行的记录锁MySQL 评估 WHERE 条件后。
也许OP可以告诉我们现状innodb_locks_unsafe_for_binlog system
变量,并且当该变量的设置更改时是否发生相同的锁定。
另外,如果相同的锁定发生在不连续的 id 上,例如18
and 20
, or 18
and 99
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)