两个事务无法在同一表MySql

时间:2019-03-29 10:27:39

标签: mysql locking innodb isolation-level

我有两次会议。

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

2 个答案:

答案 0 :(得分:1)

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

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

在您的示例中,一个查询针对行WHERE i = 1,而另一查询针对行WHERE i = 2,则它们将不会返回相同的行,但是会检查一个重叠的行行集,如果i没有索引。检查行意味着InnoDB获取行,然后针对WHERE子句中的条件对其进行测试。根据条件,它可能会跳过它检查的某些行,并且仅在检查的行为true时才返回检查的行。

如果您在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

答案 1 :(得分:0)

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

  

对于索引记录,记录搜索遇到的内容,锁定行和任何关联的索引条目,就像为这些行发出UPDATE语句一样。

您的两个事务之一将首先执行FOR UPDATE,然后无论哪个成功,都将锁定整个表,因为选择是SELECT * 而没有任何{ {1}}子句。

更现实的现实情况是,一个事务锁定了一些记录以进行更新,而另一个事务同时锁定了一些其他行。在这种情况下,两个事务都可能能够同时进行。这是两个选择更新的示例,这些选择不会锁定相同的记录(假设两个结果集都不为空)

WHERE

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