普通select被卡住根本原因是等待行锁、mdl锁或全局读锁:在repeatable read下事务中select会因写事务持锁而等待;遇ddl时因mdl冲突卡在waiting for table metadata lock;执行flush tables with read lock后所有新select均挂起为waiting for global read lock。

普通 SELECT 本身不加行锁,但会被卡住——根本原因不是它自己锁了谁,而是它在等别人释放锁,或者被元数据锁/全局锁拦住了。
为什么 SELECT 等待行锁?
关键前提:这条 SELECT 在事务中执行,且隔离级别是 REPEATABLE READ(MySQL 默认)。
- 如果它读取的行正被另一个未提交事务用
UPDATE或DELETE锁住,SELECT就会进入等待队列 - 尤其当那个写操作没走索引、扫描了大量行,锁范围就更大,连带阻塞更多
SELECT -
EXPLAIN看不出问题——它只告诉你“怎么查”,不反映“能不能查”
为什么 SELECT 卡在 Waiting for table metadata lock?
这是元数据锁(MDL)冲突,和行锁无关,但更隐蔽、影响面更广。
- 常见触发点:
ALTER TABLE、DROP INDEX、TRUNCATE TABLE正在执行,且耗时长 - 哪怕只是个简单
SELECT * FROM t WHERE id = 1,只要表t正被 DDL 修改,就会卡住 - 验证方式:
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_NAME = 't' AND LOCK_STATUS = 'PENDING' - 注意:
mysqldump --single-transaction遇到大表ALTER时,dump 进程自身也会被反向阻塞
为什么 SELECT 卡在 Waiting for global read lock?
这不是并发问题,是人为全局锁导致的“全库冻结”。
- 根源命令:
FLUSH TABLES WITH READ LOCK,执行后所有新进来的SELECT都会挂起 - 典型误用场景:运维备份脚本没配超时、旧版迁移工具自动加锁、DBA 执行后忘记
UNLOCK TABLES - 判断依据:
SHOW PROCESSLIST中能看到状态为Waiting for global read lock的线程,且Info字段含FLUSH或为空 - 从库 IO 线程也会同步卡住,因为该命令会写入 binlog
真正容易被忽略的是:SELECT 被卡住时,往往既不是 SQL 写得差,也不是索引没建好,而是你没意识到它正站在 MDL 锁或全局读锁的排队队伍里——而这两个锁,EXPLAIN 和慢日志都看不到。











