不会,普通只读事务(begin + select)在 innodb 中默认不加任何行锁;其通过 mvcc 快照读实现非阻塞读,仅构建 read view,不触发行级锁,但需注意隐式当前读、全表扫描或 serializable 隔离级别等例外场景。

不会,普通只读事务(BEGIN + SELECT)在 InnoDB 中默认不加任何行锁。
为什么普通 SELECT 不会触发行锁
InnoDB 的行锁是“按需加锁”,不是“事务一开就锁”。只有显式请求锁的语句才会真正持有行级锁,比如:
-
SELECT ... FOR UPDATE→ 加排他锁(X 锁) -
SELECT ... LOCK IN SHARE MODE→ 加共享锁(S 锁) -
UPDATE/DELETE/INSERT→ 隐式加 X 锁
而裸 SELECT(无锁提示、无写操作)在 REPEATABLE READ(默认隔离级别)下只构建 read view,用于 MVCC 快照读,不触发行锁机制。它可能持有意向锁(IS 或 IX),但这是轻量级表级元数据锁,不影响并发读写。
什么情况下只读事务会意外持锁
看似只读,实则已锁 —— 这类情况容易被忽略:
- 事务中执行了
SELECT ... FOR UPDATE或LOCK IN SHARE MODE,哪怕只有一条,整个事务就进入“锁定状态” - 查询走全表扫描且隔离级别为
SERIALIZABLE:该级别下所有SELECT都隐式等价于SELECT ... LOCK IN SHARE MODE - 使用了
SELECT INTO OUTFILE或调用含写副作用的函数(如UUID()在某些版本中触发内部写),MySQL 会报错ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION,但在此之前可能已尝试加锁 - 事务未
COMMIT或ROLLBACK,长期空闲挂起,read view持续存在,间接拖慢 purge 线程清理旧版本,造成“伪锁压”(非真实行锁,但影响性能)
如何验证当前事务是否持锁
别猜,直接查:
- 看活跃锁:
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_STATE = 'RUNNING';
重点关注TRX_ROWS_LOCKED和TRX_ROWS_MODIFIED,二者都为 0 表明没持行锁 - 看锁等待:
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
有记录说明已有锁冲突 - 看锁详情:
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
(MySQL 8.0+ 已弃用,改用performance_schema.data_locks) - 用
SHOW ENGINE INNODB STATUS\G查TRANSACTIONS和LOCK WAIT段,最直观
真正容易被忽视的不是“会不会锁”,而是“事务开着却不关”——一个没提交的只读事务,会让 read view 一直活着,阻止 undo 日志回收,最终拖慢整个实例的 purge 效率。显式用 START TRANSACTION READ ONLY 并及时结束,才是轻量只读的正确姿势。











