alter trigger 会隐式加元数据锁(mdl),全程持有直至语句结束,若表上有长事务则被阻塞,导致后续dml/ddl排队;myisam下更会升级为重量级表锁,整表不可读写。

触发器修改本身不锁表,但 ALTER TRIGGER 会隐式加元数据锁(MDL)
MySQL 修改触发器用的是 ALTER TRIGGER 语句,它不直接操作数据,但必须对触发器所属的表加元数据锁(MDL)。这个锁在语句执行全程持有,直到事务提交或语句结束。如果此时有长事务正在读/写该表(比如一个未提交的 SELECT ... FOR UPDATE 或大范围 UPDATE),ALTER TRIGGER 就会被阻塞,表现为“卡住”,而后续所有对该表的 DML/DDL 也会排队等待——看起来像整张表被锁死。
常见现象:
-
SHOW PROCESSLIST中看到状态为Waiting for table metadata lock - 目标表的
SELECT、INSERT都变慢甚至超时 -
information_schema.INNODB_TRX里查不到活跃事务,但锁等待仍在持续
触发器定义变更可能间接引发行锁升级
修改触发器逻辑(比如把 BEFORE INSERT 里原本只做校验的代码,改成新增一条关联记录),会让触发器执行时多一次 INSERT INTO logs 或 UPDATE counters。如果这些操作没走索引、或目标表本身是 MyISAM、或触发器里用了 SELECT ... FOR UPDATE,就可能从原本的行锁变成间隙锁甚至全表扫描锁。
关键点:
- InnoDB 下,触发器继承父语句的隔离级别和锁行为,不是独立事务
- 触发器里任何 DML 都会延长原事务的锁持有时间,增加冲突概率
- 若触发器调用的存储过程含显式
START TRANSACTION,MySQL 直接报错ERROR 1305 (42000): SAVEPOINT does not exist,但错误前已持锁
MyISAM 表上改触发器 = 实质性表锁风险更高
MyISAM 引擎不支持行锁,所有写操作默认加表级写锁。哪怕只是 ALTER TRIGGER,MySQL 仍需确保触发器与表结构的一致性,因此会申请表写锁(Table Write Lock)。只要有一个慢查询或长事务正持有该表的读锁(Table Read Lock),ALTER TRIGGER 就得等;反过来,一旦 ALTER TRIGGER 拿到写锁,其他所有读写请求全部阻塞。
对比 InnoDB:
- InnoDB 的 MDL 是轻量级的,只阻塞同表的 DDL,DML 一般不受影响(除非被锁等待链拖住)
- MyISAM 的表锁是重量级的,
ALTER TRIGGER期间整张表不可读不可写 - 线上若混用引擎,务必确认触发器所在表的
ENGINE类型:SHOW CREATE TABLE orders;
真正危险的是“改完立刻生效”带来的不可控连锁反应
触发器没有灰度发布机制。一旦 ALTER TRIGGER 成功,新逻辑立即对所有后续 DML 生效。如果新逻辑里有未预估的查询(比如 JOIN 了大表)、或触发条件比原来更宽泛(比如从 WHERE id > 1000 改成 WHERE status != 'archived'),就可能让原本毫秒级的语句变成秒级甚至分钟级,锁持有时间指数增长。
容易被忽略的细节:
- 触发器里的 SQL 不会出现在慢查询日志(
slow_query_log)中,除非显式开启log_slow_slave_statements或使用性能模式(performance_schema)抓取 -
EXPLAIN对触发器内部语句无效,只能靠SHOW PROFILE或sys.statement_analysis反向定位 - 测试环境改触发器没问题,不代表生产安全——数据量、并发压力、事务分布完全不同











