两个事务无法在同一 table、MySql 上获得 IX 锁

Two transactions cannot get IX lock on same table, MySql

我有两个会话在做这个。

Session 1>start transaction;
Session 1>select * from account for update;

Session 2>start transaction;
Session 2>select * from account for update; //waiting.

既然IX锁与IX锁兼容,不应该会话二也无需等待就获得IX锁。

即使下面的一组语句也不起作用。

Session 1>start transaction;
Session 1>select * from account where i = 1 for update;

Session 2>start transaction;
Session 2>select * from account where i = 2 for update; //waiting.

隔离级别为Repeatable Read。这个隔离级别有什么不同吗?

兼容性来源this

当使用 SELECT ... FOR UPDATE 时,MySQL 将尝试在查询 return 的所有行和关联的索引条目上获取 IX(意向排他锁)锁。来自 documentation:

For index records the search encounters, locks the rows and any associated index entries, the same as if you issued an UPDATE statement for those rows.

你的两个事务之一将首先执行 FOR UPDATE,无论哪个成功,然后将锁定整个 table,因为 select 是一个 SELECT * 没有任何WHERE子句。

一个更现实的现实世界场景是一个事务锁定一些记录以进行更新,而另一个事务同时锁定其他一些行的情况。在这种情况下,两笔交易可能会同时进行。下面是两个 select 的更新示例,它不会锁定相同的记录(假设两个结果集都不为空)

SELECT * FROM yourTable WHERE id = 1 FOR UPDATE;
SELECT * FROM yourTable WHERE id = 2 FOR UPDATE;

如果以上仍然会看到第二个稍后的事务阻塞,那可能是因为 MySQL 有时也可以在实际目标记录之后锁定间隙。

您显示的两个查询获取 table 的 IX 锁,它们彼此兼容。

但是 SELECT...FOR UPDATE 也继续获取它检查的行的 X 锁。这些都是冲突,这就是第二个 SELECT...FOR UPDATE 等待的原因。

在您的示例中,一个查询针对行 WHERE i = 1,另一个查询针对行 WHERE i = 2,它们不会 return 相同的行,但它们会 examine 如果 i 没有索引,则一组重叠的行。检查行意味着 InnoDB 获取行,然后根据 WHERE 子句中的条件测试它们。根据条件,它可能会跳过它检查的一些行,并且它只检查 returns 行,如果它们测试为真。

如果您在 i 上有索引,InnoDB 可以排除不匹配的行 而无需 检查这些行。在这些条件下,您的示例将不会显示冲突。

Session 1>ALTER TABLE account ADD INDEX (i);

Session 1>start transaction;
Session 1>select * from account where i = 1 for update;

Session 2>start transaction;
Session 2>select * from account where i = 2 for update; // does NOT wait