触发器执行顺序由类型和定义顺序共同决定
MySQL 中同一事件(如
INSERT)上可以定义多个触发器,但它们的执行顺序不是随机的:先执行
BEFORE类型,再执行
AFTER类型;同为
BEFORE或同为
AFTER时,按创建时间升序执行(即先建的先触发)。没有显式控制执行优先级的语法(比如
DEFINER或
ORDER子句),
CREATE TRIGGER语句本身不支持指定顺序。
无法用 SQL 直接修改已有触发器的执行顺序
MySQL 不提供
ALTER TRIGGER语法,也不能给触发器设权重或序号。若需调整顺序,只能删掉再重建——但要注意依赖关系:
DROP TRIGGER IF EXISTS后必须立刻用新顺序
CREATE TRIGGER,否则中间窗口可能被业务写入绕过逻辑 重建前应确认该触发器未被其他存储过程或应用逻辑隐式依赖(例如依赖其生成的临时状态) 在主从复制环境中,
DROP+
CREATE可能导致从库触发器缺失或顺序错乱,建议在维护窗口停写并校验
BEFORE 触发器中修改 NEW 值会影响后续触发器和主语句
只有
BEFORE INSERT和
BEFORE UPDATE允许赋值给
NEW.column_name,这个修改会透传给后续所有同类型触发器,最终也作用于实际插入/更新的数据。而
AFTER触发器读到的
NEW已是最终值,不能修改。
CREATE TRIGGER tr_before_insert_user BEFORE INSERT ON users FOR EACH ROW BEGIN SET NEW.created_at = NOW(); SET NEW.status = IF(NEW.status IS NULL, 'active', NEW.status); END;
如果还有另一个
BEFORE INSERT触发器在它之后创建,它看到的
NEW.created_at就已是
NOW()的值,而非原始
NULL。
嵌套触发器与性能风险需主动规避
MySQL 默认允许触发器内执行 DML 并再次激活其他触发器(即嵌套),最大深度由系统变量
max_sp_recursion_depth控制(默认 0,表示不限制)。这容易引发意料外的连锁反应: 一个
UPDATE触发器里更新了另一张表,恰好那张表也有
AFTER UPDATE触发器,就构成嵌套 递归无终止条件时可能快速耗尽栈空间,报错
ERROR 1456 (HY000): Recursive limit 0 (as set by the max_sp_recursion_depth variable)嵌套层级越深,事务锁持有时间越长,死锁概率上升
生产环境建议显式设限:
SET GLOBAL max_sp_recursion_depth = 1;,强制只允许一级触发(主语句 → 触发器),避免隐式扩散。 触发器顺序不可动态调度,依赖顺序的逻辑必须靠建模阶段严格约定;真正复杂的流程协调,应该移出数据库层,交给应用或工作流引擎处理。
