MySQL触发器内的SELECT默认不加锁,仅做MVCC快照读;若显式使用FOR UPDATE或LOCK IN SHARE MODE,或事务隔离级别为SERIALIZABLE,则分别加X锁、S锁或隐式S锁。

不会自动加读锁,但SELECT语句在触发器里执行时,是否加锁、加什么锁,完全取决于事务隔离级别和语句写法——不是触发器本身决定的,而是它所处的事务上下文和SQL引擎行为决定的。
MySQL中触发器里的SELECT默认不加锁
在InnoDB中,裸SELECT(无FOR UPDATE或LOCK IN SHARE MODE)在READ COMMITTED或REPEATABLE READ下都不加锁,只做一致性读(MVCC快照)。但有例外:
- 如果触发器里写了
SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE,那就会按需加X锁或S锁 - 如果触发器运行在
SERIALIZABLE隔离级别下,所有SELECT都会隐式转成LOCK IN SHARE MODE - 若触发器调用的存储函数内部含
SELECT,且该函数被标记为READS SQL DATA,仍不加锁;但若标记为MODIFIES SQL DATA,则可能因执行计划变化间接影响锁行为
SQL Server中触发器里的SELECT会加共享锁
SQL Server默认READ COMMITTED下,SELECT会申请并立即释放S锁(行/页级),但锁生命周期极短;不过以下情况会让锁“挂住”:
- 事务未提交:锁持续到事务结束,不是语句结束
- 用了
REPEATABLE READ或SERIALIZABLE:S锁会保留到事务结束,且后者可能升级为范围锁甚至表锁 - 查询走不到索引(如
WHERE字段没索引),导致全表扫描 → 所有扫描行都持S锁,等效于“锁表” - 显式加了
WITH (HOLDLOCK)或WITH (UPDLOCK),锁行为就按提示走
为什么你看到“SELECT卡住其他写入”,其实不是SELECT的问题
真正阻塞写操作的,往往不是SELECT本身,而是它所在的事务还没结束。常见诱因:
- 触发器里
SELECT后又跟了UPDATE或INSERT,整个事务变长,S锁被拖住 - 触发器嵌套调用另一个过程,而那个过程里有未提交的DML
- 应用层开启事务后,执行带触发器的DML,但迟迟不
COMMIT—— 此时哪怕触发器早跑完了,S锁还在挂着 - MyISAM引擎下,任何
SELECT都会加表级读锁(但MyISAM基本已淘汰,不建议用)
排查时别盯着触发器定义,要看实际锁状态
触发器没有独立锁视图,它的锁和主语句混在一起。查真实锁行为得看全局:
- MySQL 8.0:
SELECT * FROM performance_schema.data_locks,过滤TRX_ID对应事务,看LOCK_MODE和LOCK_DATA - SQL Server:
SELECT * FROM sys.dm_tran_locks WHERE resource_database_id = DB_ID(),重点关注request_mode(如S、U、X)和resource_type(OBJECT、PAGE、KEY) - 别信
SHOW CREATE TRIGGER——它不显示锁,只显示语法 -
EXPLAIN触发器内SQL,确认是否走索引;索引失效是让SELECT变慢、锁变多的最常见原因
最容易被忽略的是:锁持续时间永远等于事务生命周期,不是触发器执行完就释放。哪怕SELECT只花2ms,只要外面事务还开着,它持有的锁就一直挂着。

















