InnoDB SELECT ... FOR UPDATE语句锁定表中的所有行

时间:2011-07-14 08:31:11

标签: mysql transactions innodb isolation-level locks

启用了InnoDB插件的MySQL Server版本5.1.41。我有以下三张发票表:发票,invoice_components和invoice_expenses。表发票具有invoice_id主键。 invoice_components和invoice_expenses都链接到表发票,其invoice_id为非唯一foreign_key(每张发票可以包含多个组件和多个费用)。两个表都具有此外键的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

当我尝试SELECT ... FOR UPDATE其他发票及其组件和费用时也会发生同样的情况。似乎第一个事务已锁定invoice_expenses表中的所有行。任何想法为什么会发生这种情况?

其他信息

事务2在事务1的第三个查询之后启动。服务器上没有其他用户,连接或事务。

问题出现在默认的REPEATABLE READ事务隔离级别。它通过更改为READ COMMITTED级别来修复。这是一个解决方案,但它仍然无法解释为什么发票问题发生在invoice_expenses而不是invoice_components。

3 个答案:

答案 0 :(得分:10)

我怀疑它与缺口锁下一把锁以及 REPEATABLE READ 行为的差异有关:

摘录来自MySQL文档: SET TRANSACTION syntax

  

对于锁定读取(使用FOR UPDATE或LOCK IN SHARE MODE的SELECT),UPDATE和DELETE语句,锁定取决于语句是使用具有唯一搜索条件的唯一索引还是范围类型搜索条件。对于具有唯一搜索条件的唯一索引,InnoDB仅锁定找到的索引记录,而不是之前的间隙。对于其他搜索条件,InnoDB锁定扫描的索引范围,使用间隙锁或下一键(间隙加索引记录)锁来阻止其他会话插入范围所涵盖的间隙。

READ COMMITTED

  

注意:在MySQL 5.1中,如果使用了READ COMMITTED隔离级别或启用了innodb_locks_unsafe_for_binlog系统变量,除了外键约束检查和重复键检查外,没有InnoDB间隙锁定 。此外,在MySQL评估了WHERE条件之后,发布了非匹配行的记录锁

也许OP可以告诉我们innodb_locks_unsafe_for_binlog system变量的状态,以及当此变量的设置发生变化时是否发生相同的锁定。

此外,如果发生相同的锁定而不是顺序ID,例如1820,或1899

答案 1 :(得分:0)

“对于搜索遇到的索引记录,SELECT ... FROM ... FOR UPDATE阻止其他会话执行SELECT ... FROM ... LOCK IN SHARE MODE或从某些事务隔离级别读取。一致读取将忽略在读取视图“

中存在的记录上设置的任何锁定

可以通过select更新应用哪些锁定,以便其他会话无法读取锁定记录?

答案 2 :(得分:-2)

您正在使用交易; autocommit不会禁用事务,它只会使它们在没有明确start transaction的语句结束时自动提交。

正在发生的事情是,其他一些线程在一些记录上持有记录锁定(你正在更新表中的每条记录!)太长时间了,你的线程正在超时。

您可以在活动结束后发出“SHOW ENGINE INNODB STATUS”,了解该活动的更多详情。理想情况下,在安静的测试机上进行此操作。