MySQL 9.7 的 enable_cascade_triggers:级联动作会激活子表触发器,但这个变量已标弃用
MySQL 9.7.0(2026-04-21,LTS)新增了会话级系统变量 enable_cascade_triggers,默认 OFF。它控制 SQL 层处理外键时,CASCADE 和 SET NULL 动作是否在受影响的子表行上激活 BEFORE / AFTER 触发器。
SET SESSION enable_cascade_triggers = ON;
开启前,父表 DELETE 触发的子表级联删除对触发器不可见;开启后,被级联删除或置空的每一行子表记录都会走一遍子表上定义的触发器。
触发器属于发起语句,失败则整条回滚
级联引发的触发器不是独立事务,而是发起语句的一部分。触发器里报错(SIGNAL、唯一键冲突、约束校验失败等),整条语句连同它产生的级联变更一起回滚。这意味着父表上一条看似普通的 DELETE,可能因为子表触发器里的逻辑而整体失败。
复制的两种行为
- 行格式(ROW):从库只应用触发器产生的结果行变更,不重新执行触发器。触发器在源库执行一次,效果随行变更落到从库。
- 语句格式(STATEMENT):从库按源库的
enable_cascade_triggers设置重放语句。源库开启,从库也按开启处理,与从库自身的变量值无关。
混合格式下按实际记录的格式走对应路径。
反直觉的地方:它已被标记弃用
在同一份 MySQL 9.7 文档的「新增、弃用或移除的服务器与状态变量」清单里,enable_cascade_triggers 出现在 Deprecated in MySQL 9.7.0 一栏,描述为“指定外键由服务器运行时还是 InnoDB 引擎处理”。也就是说,这个功能在 9.7.0 引入的同时就被标为弃用。以官方清单为准,它更接近一个过渡开关,不适合写进长期方案;依赖它之前先确认后续小版本是否仍然保留。
落地前先核查子表触发器
先找出所有会被级联路径影响到的子表触发器:
SELECT rc.CONSTRAINT_NAME,
rc.TABLE_NAME AS child_table,
rc.REFERENCED_TABLE_NAME AS parent_table,
rc.DELETE_RULE,
t.TRIGGER_NAME,
t.ACTION_TIMING,
t.EVENT_MANIPULATION
FROM information_schema.REFERENTIAL_CONSTRAINTS rc
JOIN information_schema.TRIGGERS t
ON t.TRIGGER_SCHEMA = rc.CONSTRAINT_SCHEMA
AND t.EVENT_OBJECT_TABLE = rc.TABLE_NAME
WHERE rc.CONSTRAINT_SCHEMA = DATABASE()
AND rc.DELETE_RULE IN ('CASCADE', 'SET NULL')
AND t.EVENT_MANIPULATION IN ('DELETE', 'UPDATE');
结果里每一行都是一个“父表删除会多触发一次”的组合。
误开后的三类症状
- 审计表重复写:子表上的
AFTER DELETE往审计表插一条记录,级联删除 N 行就多出 N 条,和父表操作本身的记录混在一起。 - 计数器翻倍:触发器里对统计表做
UPDATE ... SET cnt = cnt + 1,级联路径让同一批业务变更被累计两次。 - 父表
DELETE突然报错回滚:子表触发器里的校验或SIGNAL以前不参与级联路径,开启后被拉进来,父表删除直接失败。
做法:按会话开启再评估
变量是会话级的,不必全局改动。在需要验证的会话里单独打开:
SET SESSION enable_cascade_triggers = ON;
-- 执行目标语句,观察审计表、计数器与错误日志
SET SESSION enable_cascade_triggers = OFF;
先在只读或影子库上跑一遍级联路径,确认子表触发器的副作用是否可接受,再决定是否在特定会话中启用。考虑到弃用标记,任何依赖都应保留关闭时的回退路径。
文档:https://dev.mysql.com/doc/refman/9.7/en/mysql-nutshell.html