简体   繁体   English

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

[英]InnoDB SELECT … FOR UPDATE statement locking all rows in a table

MySQL Server version 5.1.41 with InnoDB plugin enabled. MySQL 服务器版本 5.1.41,启用了 InnoDB 插件。 I have the following three tables for invoices: invoices, invoice_components and invoice_expenses.我有以下三个发票表:invoices、invoice_components 和 invoice_expenses。 Table invoices has invoice_id primary key.表 invoices 具有 invoice_id 主键。 Both invoice_components and invoice_expenses are linked to table invoices with invoice_id as a non-unique foreign_key (each invoice can have more than one component and more than one expense). invoice_components 和 invoice_expenses 都链接到表 invoices,其中 invoice_id 作为非唯一的 foreign_key(每张发票可以有多个组件和多个费用)。 Both tables have a BTREE index for this foreign key.两个表都有这个外键的 BTREE 索引。

I have the following transactions:我有以下交易:

transaction 1交易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; 

Everything works ok for the first transaction and the rows are selected and locked.第一个事务一切正常,行被选中并锁定。

transaction 2交易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; 

The second transaction returns ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction第二个事务返回ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction for the third query. ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

The same happens when I try to SELECT... FOR UPDATE other invoices and their components and expenses.当我尝试 SELECT... 更新其他发票及其组件和费用时,也会发生同样的情况。 It seems the first transaction has locked all the rows in invoice_expenses table.似乎第一笔交易已锁定 invoice_expenses 表中的所有行。 Any ideas why this is happening?任何想法为什么会发生这种情况?

Additional info附加信息

Transaction 2 starts after the third query of transaction 1. There are no other users, connections or transactions on the server.事务 2 在事务 1 的第三次查询之后开始。服务器上没有其他用户、连接或事务。

The problem occurs in the default REPEATABLE READ transaction isolation level.该问题发生在默认的 REPEATABLE READ 事务隔离级别中。 It is fixed by changing to READ COMMITTED level.它通过更改为 READ COMMITTED 级别来修复。 This is a solution but it still doesn't explain why the problem is occurring with invoice_expenses and not with invoice_components.这是一个解决方案,但它仍然无法解释为什么问题出现在 invoice_expenses 而不是 invoice_components。

I suspect it has to do with gap locks and next-key locks and the differences in the behaviour of REPEATABLE READ :我怀疑它与间隙锁下一个键锁以及REPEATABLE READ的行为差异有关:

The excerpts are from MySQL docs: SET TRANSACTION syntax摘录自 MySQL 文档: SET TRANSACTION 语法

For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE), UPDATE, and DELETE statements, locking depends on whether the statement uses a unique index with a unique search condition, or a range-type search condition.对于锁定读取(SELECT with FOR UPDATE 或 LOCK IN SHARE MODE)、UPDATE 和 DELETE 语句,锁定取决于语句是使用具有唯一搜索条件的唯一索引还是范围类型的搜索条件。 For a unique index with a unique search condition, InnoDB locks only the index record found, not the gap before it.对于具有唯一搜索条件的唯一索引,InnoDB 只锁定找到的索引记录,而不锁定它之前的间隙。 For other search conditions, InnoDB locks the index range scanned, using gap locks or next-key (gap plus index-record) locks to block insertions by other sessions into the gaps covered by the range.对于其他搜索条件,InnoDB 锁定扫描的索引范围,使用间隙锁或下一个键(间隙加索引记录)锁来阻止其他会话插入到范围所覆盖的间隙中。

and READ COMMITTED :阅读提交

Note: In MySQL 5.1, if the READ COMMITTED isolation level is used or the innodb_locks_unsafe_for_binlog system variable is enabled, there is no InnoDB gap locking except for foreign-key constraint checking and duplicate-key checking.注意:在 MySQL 5.1 中,如果使用了 READ COMMITTED 隔离级别或启用了 innodb_locks_unsafe_for_binlog 系统变量,则除了外键约束检查和重复键检查外,没有 InnoDB 间隙锁定 Also, record locks for nonmatching rows are released after MySQL has evaluated the WHERE condition.此外,在 MySQL 评估 WHERE 条件后,将释放非匹配行的记录锁

Perhaps OP can tell us the status of innodb_locks_unsafe_for_binlog system variable and if the same locking occurs when this variable's setting is changed.也许 OP 可以告诉我们innodb_locks_unsafe_for_binlog system变量的状态,以及当这个变量的设置改变时是否发生相同的锁定。

Also, if same locking happens with not sequential ids, like 18 and 20 , or 18 and 99此外,如果相同的锁定发生在不连续的 id 上,例如18201899

"For index records the search encounters, SELECT... FROM... FOR UPDATE blocks other sessions from doing SELECT... FROM... LOCK IN SHARE MODE or from reading in certain transaction isolation levels. Consistent reads will ignore any locks set on the records that exist in the read view" “对于搜索遇到的索引记录,SELECT...FROM...FOR UPDATE 阻止其他会话执行 SELECT...FROM...锁定共享模式或读取某些事务隔离级别。一致的读取将忽略任何锁定在读取视图中存在的记录上设置"

What are those certain locks which can be applied with select for update so that other sessions cannot read locked record?哪些特定的锁可以与 select 一起应用以进行更新,以使其他会话无法读取锁定的记录?

You are using a transaction;您正在使用交易; autocommit does not disable transactions, it just makes them automatically commit at the end of the statements that do not have an explicit start transaction on them. autocommit 不会禁用事务,它只是让它们在没有显式start transaction的语句结束时自动提交。

What is happening is, some other thread is holding a record lock on some record (you're updating every record in the table,) for too long.正在发生的事情是,其他一些线程在某条记录上持有记录锁(您正在更新表中的每条记录)太久。 and your thread is being timed out.并且您的线程正在超时。

You can see more details of the event by issuing a "SHOW ENGINE INNODB STATUS" after the event.您可以通过在事件之后发出“SHOW ENGINE INNODB STATUS”来查看事件的更多详细信息。 Ideally do this on a quiet test-machine.理想情况下,在安静的测试机器上执行此操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM