触发器路由核心是用NEW字段做条件分支判断目标子表,如YEAR(NEW.created_at)或MOD(NEW.id,4),但须写死表名、提前建好结构一致的子表,并确保主键/AUTO_INCREMENT错开;禁止在触发器中修改NEW值,数据清洗应前置到应用层或存储过程,推荐优先使用MySQL原生分区或应用层路由替代触发器。

触发器里怎么判断插入数据该进哪张子表
核心是用 NEW 引用新行字段,在触发器体中做条件分支。比如按年份分表,就取 NEW.created_at 的年份;按用户ID哈希分表,就用 MOD(NEW.user_id, 4) 算出目标表编号。
注意:MySQL 触发器不支持动态表名(如 INSERT INTO CONCAT('orders_', year) ...),必须写死 INSERT INTO orders_2023 这类语句——所以分表数量得提前规划好,不能靠触发器自动建表。
- 用
CASE WHEN或嵌套IF控制路由逻辑,避免漏掉分支导致插入失败 - 所有分支的
INSERT字段顺序、类型、是否允许 NULL 必须和主表严格一致 - 如果条件依赖函数(如
YEAR(NEW.created_at)),确保该函数在触发器上下文中可执行(部分 MySQL 版本对非确定性函数有限制)
MySQL触发器无法修改NEW值,那怎么处理路由前的数据清洗
触发器里不能赋值给 NEW.column(会报错 Can't update table in stored function/trigger),所以字段改写、默认值填充、脱敏等操作必须在 INSERT 语句发出前完成。
可行做法是:把业务层或中间件作为“前置过滤点”,或者用存储过程封装完整逻辑(先处理 NEW 数据,再调用对应子表的 INSERT)。触发器只做纯路由,不碰数据内容。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 不要在触发器里尝试
SET NEW.status = 'pending'—— 这会直接报错 - 若必须补默认值,建议在子表定义时设
DEFAULT,而非依赖触发器注入 - 敏感字段脱敏(如手机号掩码)应在应用层或视图层处理,触发器不是合适位置
为什么INSERT触发器路由后查不到数据,或主键冲突
常见原因是子表缺失主键或索引结构不一致。触发器只是把语句转到另一张表执行,但那张表如果没建好约束,就会静默失败或违反规则。
- 所有子表必须有和主表完全相同的主键定义(包括字段名、类型、
AUTO_INCREMENT属性) - 若主表主键是自增,子表也得是自增,且各子表的
AUTO_INCREMENT起始值要错开(例如子表1从1开始、子表2从1000001开始),否则多表并发插入会撞ID - 检查子表是否存在:触发器不会自动创建表,
INSERT INTO orders_2024时如果表不存在,会报错Table 'db.orders_2024' doesn't exist - 事务行为:触发器内
INSERT和原语句在同一个事务中,任一失败则全部回滚——这点容易被忽略,调试时要查完整错误日志,别只看最外层返回
替代方案比触发器更可控:应用层路由 or 分区表
触发器路由本质是把分表逻辑藏在数据库侧,看似透明,实则难监控、难测试、难迁移。线上出问题时,连 EXPLAIN 都看不到实际执行哪张子表。
更推荐的做法是:用应用代码明确决定插入 orders_2023 还是 orders_2024,或者直接用 MySQL 原生分区(PARTITION BY RANGE (YEAR(created_at)))。后者由引擎自动路由,无需触发器,且 SELECT 也能自动裁剪分区。
- 分区表不支持全文索引、不支持某些外键场景,但对时间/ID范围分片足够友好
- 应用层路由需要统一 SDK 封装,但可打日志、埋点、熔断、灰度,运维友好得多
- 触发器一旦启用,
mysqldump默认不导出触发器逻辑,备份恢复后容易漏配,导致新库插入全进主表










