MySQL单表最多支持6个触发器:每个事件(INSERT、UPDATE、DELETE)各对应BEFORE和AFTER两种时机。触发器名全局唯一,不可重名;触发器中禁止修改当前表;其操作随主事务回滚,不支持显式事务控制。

一个表最多能建几个触发器
MySQL 允许对同一张表为每个触发事件(INSERT、UPDATE、DELETE)分别定义 BEFORE 和 AFTER 两种时机的触发器,因此单表最多支持 6 个触发器:
BEFORE INSERTAFTER INSERTBEFORE UPDATEAFTER UPDATEBEFORE DELETEAFTER DELETE
CREATE TRIGGER 会报错:ERROR 1359 (HY000): Trigger already exists(注意:该错误码实际表示“同名触发器已存在”,但更常见的是因命名冲突或重复创建导致;真正超限会报 ERROR 1235 (42000): This version of MySQL doesn't yet support 'multiple triggers with the same action time and event for one table' —— 这在 5.7+ 已解除限制,但逻辑上限仍是 6)。
触发器命名必须全局唯一
MySQL 不允许同名触发器存在,哪怕在不同表上也不行。触发器名作用域是整个数据库(SCHEMA),不是表级。这意味着:
- 不能有两个触发器都叫
tr_log_after_insert,哪怕一个在users表、一个在orders表 - 建触发器时若不显式指定名字,MySQL 不会自动生成;你必须写
CREATE TRIGGER xxx BEFORE INSERT ...,xxx是必需字段 - 重命名触发器不可行,只能先
DROP TRIGGER IF EXISTS old_name,再重建
触发器中不能修改当前表
这是最容易踩的坑:BEFORE 或 AFTER 触发器里,如果尝试对「正在被触发的同一张表」做 INSERT/UPDATE/DELETE,会直接报错:
ERROR 1442 (HY000): Can't update table 't1' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
典型误用场景:
- 在
BEFORE INSERT ON orders中试图往orders表插入一条日志记录 - 在
AFTER UPDATE ON users中又去UPDATE users设置某个字段(比如updated_at)
解决办法只有两个:
- 把需要写入的数据转存到另一张日志表(如
orders_log),确保表名不同 - 改用应用层逻辑或定时任务补全,避开触发器内写本表
触发器不支持事务回滚中的自动清理
触发器本身运行在主语句的事务上下文中,但它执行的语句也会参与事务。问题在于:如果主语句最终 ROLLBACK,触发器里做的变更(比如往日志表插数据)也会一起回滚 —— 听起来合理?但隐患在于:
- 如果你在触发器里调用了存储过程,而该过程内部有
START TRANSACTION或COMMIT,会直接报错:ERROR 1305 (42000): SAVEPOINT does not exist或更常见的Explicit or implicit commit is not allowed in stored function or trigger - 触发器无法感知主事务是否将要失败,也无法注册
ON ROLLBACK回调 - 想实现“不管主事务成败都要记日志”,只能用
INSERT DELAYED(已弃用)或外部消息队列,MySQL 原生不支持
所以别指望触发器能当可靠审计日志的兜底方案;真要强一致性,得靠应用层双写 + 本地消息表 + 补偿任务。










