MySQL触发器中禁止使用PREPARE/EXECUTE动态建表,因引擎限制而非权限问题;须预建分表、显式字段插入、合理选用BEFORE/AFTER时机,并控制路由逻辑与索引以保障性能与数据一致性。

触发器里不能用 PREPARE / EXECUTE 动态建表
MySQL 触发器执行时处于语句上下文锁定状态,不支持动态 SQL。想在 INSERT 触发器里根据时间自动创建新分表(比如 t_log_202410),会直接报错:ERROR 1336 (0A000): Dynamic SQL is not allowed in stored function or trigger。这不是权限问题,是引擎限制。
实操建议:
- 分表动作必须提前完成——上线前按业务周期(如月/周)预建好未来 6–12 个月的表,命名统一,如
t_log_202410、t_log_202411 - 触发器只做路由判断和 INSERT 转发,不参与建表
- 用外部脚本(Python/Shell)定期检查并补全缺失分表,配合 cron 执行,别塞进触发器
INSERT ... SELECT 路由到目标分表时字段顺序必须严格一致
垂直分表后,主表和子表字段不完全重叠,但触发器里用 INSERT INTO t_log_202410 (...) SELECT ... 转发时,若字段名、数量、顺序稍有出入,就会静默丢数据或报错 Column count doesn't match value count。
常见错误现象:
- 主表加了新字段,但没同步到所有分表结构,触发器 INSERT 失败
- SELECT 子句漏写某个非空字段,导致插入失败(尤其
NOT NULL字段) - 用了
*—— 绝对禁止!字段顺序不可控,且不同分表结构可能已分化
实操建议:
- 触发器中显式列出所有目标字段:
INSERT INTO t_log_202410 (id, uid, content, created_at) SELECT NEW.id, NEW.uid, NEW.content, NEW.created_at - 所有分表用相同 DDL 初始化,后续字段变更必须原子化同步(用 pt-online-schema-change 或带锁升级)
- 用视图或存储过程封装 INSERT 逻辑?不行——触发器里不能调用含 SQL 的存储过程
NEW 和 OLD 在 BEFORE/AFTER 触发器中的可用性差异
垂直分表常需在插入前生成 ID 或补全字段(比如把 created_at 设为 NOW()),这时必须用 BEFORE INSERT;若想基于刚插入的主键再写日志,则得用 AFTER INSERT。但两者能访问的记录状态完全不同。
关键区别:
-
BEFORE INSERT:可读写NEW.*,修改它会影响最终插入值;OLD.*不可用 -
AFTER INSERT:NEW.*只读(主键已生成,但其他字段不可改);OLD.*不可用;无法再修改当前行
典型误用:
- 在
AFTER INSERT里试图赋值NEW.created_at = NOW()→ 报错Can't update table in stored function/trigger - 在
BEFORE INSERT里读OLD.id→ 值为空,因为还没插入
实操建议:
- 需要预处理字段(如生成时间、脱敏、ID 映射),一律用
BEFORE INSERT - 需要依赖自增主键做关联写入(比如往分表 + 关联日志表各插一条),用
AFTER INSERT,但只能读NEW.id,不能改
触发器性能损耗集中在单条 INSERT 上,高并发下容易成为瓶颈
每个触发器执行都是同步阻塞的,哪怕只是简单判断月份再 INSERT 到另一张表,也会增加约 0.2–0.8ms 延迟(实测 InnoDB,SSD)。当 QPS 超过 500,延迟抖动明显,慢查询日志里大量出现 Trigger execution 占比过高。
影响因素:
- 分表数量越多,
CASE WHEN分支越长,判断耗时越明显 - 目标分表未建好索引(比如缺
(uid, created_at)联合索引),INSERT 本身变慢,触发器等得更久 - 触发器里嵌套调用函数(哪怕只是
YEAR(NEW.created_at))会额外开销
实操建议:
- 路由逻辑尽量扁平:用
CONCAT('t_log_', DATE_FORMAT(NEW.created_at, '%Y%m'))拼表名,别写十几层IF ... ELSEIF - 所有分表确保主键、高频查询字段都有对应索引,避免 INSERT 触发二次索引维护风暴
- 单表日均写入超 50 万行?该考虑应用层分库分表了,别硬扛触发器
垂直分表+触发器路由看着轻量,但表名拼接、字段对齐、时机选择、性能毛刺这四点,任何一个疏忽都会让数据错位或服务卡顿。最麻烦的是问题往往延迟暴露——数据进了错表,查不出来,还不好回溯。



















