我们在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
答案 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 MODE
或SELECT ... FOR UPDATE
,当然会锁定。
但是当您SELECT
作为数据修改语句(如INSERT INTO...SELECT
)或UPDATE
的子查询或SET @variable := (SELECT...)
中找到的一部分时,{{1}}使用共享锁来确保数据在更新过程中不会发生变化。
文档可能不完整。更好地测试。
答案 1 :(得分:0)
您的第一个语句在表上执行SELECT,因此事务获取一行的读锁定。
第二个事务尝试在同一个表上获取写锁(在所有行上,因为没有WHERE
子句),但不能。
您需要在 < / p>
COMMIT
之后发出ROLLBACK
(或SET @x = (...)
)命令,以便释放读锁定。
上面的错误。我保留这篇文章只是因为以下评论可能会引起关注。