在MySQL中为什么在使用read uncommitted时从select中设置变量获取锁?

时间:2012-10-31 05:37:02

标签: mysql locking transaction-isolation read-uncommitted

我们在MySQL中使用InnoDB有一个表,我们使用的是事务隔离级别read uncommitted。为什么如图所示设置@x会获得锁定?

mysql> set @x = (select userID from users limit 1);
Query OK, 0 rows affected (0.02 sec)

mysql>

尝试从另一个提示符更新此表会导致超时错误:

mysql> update users set userID = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

2 个答案:

答案 0 :(得分:2)

对于它的价值,此锁定不仅限于READ-UNCOMMITTED

mysql1> show variables like '%isolation%';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| tx_isolation  | REPEATABLE-READ |
+---------------+-----------------+
mysql1> BEGIN;
mysql1> SET @x := (SELECT x FROM foo LIMIT 1);

mysql2> UPDATE foo SET x = x+1;
[gets a lock wait]

mysql3> SHOW ENGINE INNODB STATUS;
...
---TRANSACTION 228746, ACTIVE 22 sec
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 58, OS thread handle 0x7fc262a1c700, query id 8163
  192.168.56.1 root cleaning up
TABLE LOCK table `test`.`foo` trx id 228746 lock mode IS
RECORD LOCKS space id 801 page no 3 n bits 80 index `PRIMARY` 
  of table `test`.`foo` trx id 228746 lock mode S
...

正如您记录的错误Bug #67452 Setting a variable from a select acquires a lock when using read uncommitted中所讨论的,此行为可能是设计使然。它似乎与SELECT语句属于同一类别,其结果用于修改数据,如下所述:

http://dev.mysql.com/doc/refman/5.6/en/innodb-locks-set.html

  

在构造REPLACE INTO t SELECT ... FROM s WHERE ...UPDATE t ... WHERE col IN (SELECT ... FROM s ...)中使用SELECT时,InnoDB会在表s的行上设置共享的下一键锁定。

下一键锁定的原因是使SELECT结果更稳定。也就是说,我们不希望SELECT匹配的行在用于UPDATE或其他数据修改语句时发生更改。

即使tx_isolation为REPEATABLE-READ,这也很重要,因为InnoDB在REPEATABLE-READ语句作为任何类型的一部分执行时都不支持SELECT语句UPDATE


重新评论:

无论文档如何,都会发生以下情况:

当您执行普通SELECT语句时,InnoDB不会在除SERIALIZABLE之外的任何事务隔离中锁定任何内容。

如果您执行SELECT ... LOCK IN SHARE MODESELECT ... FOR UPDATE,当然会锁定。

但是当您SELECT作为数据修改语句(如INSERT INTO...SELECT)或UPDATE的子查询或SET @variable := (SELECT...)中找到的一部分时,{{1}}使用共享锁来确保数据在更新过程中不会发生变化。

文档可能不完整。更好地测试。

答案 1 :(得分:0)

您的第一个语句在表上执行SELECT,因此事务获取一行的读锁定。

第二个事务尝试在同一个表上获取写锁(在所有行上,因为没有WHERE子句),但不能。

您需要在COMMIT之后发出ROLLBACK(或SET @x = (...))命令,以便释放读锁定。 < / p>

上面的错误。我保留这篇文章只是因为以下评论可能会引起关注。