REPEATABLE-READ隔离级别下LOCK TABLES WRITE阻塞SELECT的原因咨询
当前处于REPEATABLE-READ隔离级别,原本以为任何隔离级别下的所有SELECT操作都是非阻塞的,但遇到了如下场景:
会话1操作
mysql> lock tables users write; Query OK, 0 rows affected (0.00 sec)
会话2操作
mysql> begin; Query OK, 0 rows affected (0.00 sec) mysql> select * from users where id = 1; // 这里一直等待
直到会话1执行解锁:
mysql> unlock tables; Query OK, 0 rows affected (0.00 sec)
会话2的SELECT才返回结果:
mysql> select * from users where id = 1; +----+-----------------+--------------------+------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+------------+ | id | name | email | rol | email_verified_at | password | remember_token | created_at | updated_at | deleted_at | +----+-----------------+--------------------+------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+------------+ | 1 | Bella Lueilwitz | orlo19@example.com | NULL | 2022-08-01 17:22:29 | $2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi | MvMlaX9TQj | 2022-08-01 17:22:29 | 2022-08-01 17:22:29 | NULL | +----+-----------------+--------------------+------+---------------------+--------------------------------------------------------------+----------------+---------------------+---------------------+------------+ 1 row in set (10.51 sec)
这和之前看到的一个场景相反,想知道自己忽略了什么?
你混淆了MySQL服务器层的表级锁和InnoDB存储引擎的行级锁/事务隔离级别这两个完全不同的锁机制:
LOCK TABLES ... WRITE是服务器层的排他表锁
一旦某个会话执行LOCK TABLES users WRITE,会获取该表的独占排他锁,此时MySQL服务器会直接拦截其他所有会话对该表的任何读写请求——不管你用什么事务隔离级别,也不管是行级查询还是全表查询,都会被阻塞直到锁释放。这是服务器层面的全局锁,优先级远高于存储引擎的行级锁。你之前的认知误区
你以为“任何隔离级别下SELECT都非阻塞”,这个结论只适用于InnoDB行级锁场景:比如在REPEATABLE-READ级别下,普通SELECT用快照读(MVCC),不会被行级写锁阻塞。但这个规则完全不适用于服务器层的表级锁,因为表级锁直接阻断了整个表的访问路径,根本轮不到存储引擎的MVCC逻辑生效。关于你提到的相反场景
那个场景大概率存在特殊前提:比如操作的是MyISAM引擎(但MyISAM写锁同样会阻塞读)、会话未正确持有锁、或者使用了特殊的读语法(比如SELECT ... /*+ SKIP_LOCKED */这类跳过锁的扩展语法),但默认情况下LOCK TABLES ... WRITE必然会阻塞其他会话的SELECT,你的实际测试结果才是符合MySQL标准行为的。
内容的提问来源于stack exchange,提问作者user19551894

