触发器里别查表,尤其是大表
MySQL 触发器在
INSERT/
UPDATE/
DELETE语句执行期间同步运行,没有异步机制。如果触发器里写了
SELECT去查另一个大表(比如查用户信息、查配置表),就会卡住主事务,拖慢整个操作。更糟的是,这种查询还可能引发锁等待——特别是查的表正在被其他事务修改时。 尽量只用
NEW和
OLD引用本行字段,避免额外
SELECT真要关联数据,优先考虑把必要字段冗余到当前表(比如在订单表里存
user_id和
user_name,而不是每次去
users表查) 如果必须查外部表,确认该表有合适索引,且查询条件能命中索引最左前缀
避免在触发器里调用存储过程或自定义函数
看起来封装好了,但实际会放大开销。每个函数调用都是一次上下文切换,如果函数内部还有循环、临时表或复杂逻辑,性能损耗会指数级上升。尤其当触发器作用于批量操作(如
INSERT INTO ... SELECT或
LOAD DATA)时,函数会被反复执行 N 次。 把简单计算逻辑直接写进触发器体,比如
NEW.updated_at = NOW()复杂业务逻辑一律移到应用层或定时任务里处理 如果非要用函数,确保它是
DETERMINISTIC且不含 SQL,否则 MySQL 可能拒绝创建触发器
检查触发器是否被重复创建或误加了调试语句
线上环境偶尔会因部署脚本问题,导致同一个触发器被重复创建多次(虽然 MySQL 不允许同名触发器,但不同库/不同事件类型容易混淆)。更常见的是开发时加的
SELECT 'debug'或
INSERT INTO debug_log没删干净——这些语句在高并发下会迅速成为瓶颈。 查看触发器定义:执行
SHOW CREATE TRIGGER trigger_name确认触发器数量合理:
SELECT * FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_TABLE = 'your_table';删除无用触发器:
DROP TRIGGER IF EXISTS trigger_name;所有日志类语句必须清理,触发器不是调试工具
批量操作时触发器开销会被放大
单条
INSERT慢一点可能不明显,但
INSERT INTO t1 SELECT * FROM t2 LIMIT 10000这种操作会让触发器执行一万次。此时哪怕每次只花 0.5ms,总耗时也到 5 秒,还会阻塞表锁或行锁。 对大批量导入/同步场景,先
DISABLE TRIGGER(MySQL 8.0.19+ 支持
ALTER TABLE ... DISABLE TRIGGER),处理完再启用 更稳妥的做法是绕过触发器:用应用层或存储过程完成等效逻辑,再一次性写入结果 如果必须保留触发器行为,考虑改用物化视图(MySQL 不原生支持,可用汇总表 + 定时任务模拟)或 CDC 工具(如 Debezium)做后置处理
触发器本质是“隐式耦合”,它让数据库行为变得不可见、难测试、难压测。真正难优化的往往不是语法本身,而是没人意识到:那条看似无害的
SELECT正卡在每一条
UPDATE的关键路径上。
