触发器本身不占连接,但调用外部接口会拖垮mysql致假死,使应用连接池“看似”爆满;根源是sp_head内存暴涨和同步阻塞,需查triggers数量及memory/sql/sp_head::main_mem_root占用,设table_open_cache_instances=1降内存90%+,并移除触发器中所有外部调用。

TRIGGERS 本身不占连接,但调用外部接口会把 MySQL 拖进假死状态,最终让应用端连接池“看起来”爆满——这不是连接没还,是 MySQL 已经没力气接新连接了。
触发器里做 HTTP 调用会卡住整个 MySQL 实例
MySQL 的触发器运行在服务端线程内,且是同步阻塞执行的。一旦你在 BEFORE INSERT 或 AFTER UPDATE 里写 curl、调用 UDF、或通过 Sys_exec 启动外部进程:
- 所有后续对该表的 DML 操作都会排队等待这个触发器返回
- 每个触发器执行都需加载一份
sp_head结构体(内存开销约 1–2 MiB) - 高并发下,200+ 触发器 × 多 cache instance →
memory/sql/sp_head::main_mem_root占用飙升至 500MiB+ - MySQL 响应变慢,
SHOW PROCESSLIST里大量线程卡在Updating或Waiting for table metadata lock - 应用层不断重试获取连接,HikariCP 的
active持续打满,pending线程堆积
这和“连接泄漏”现象高度相似,但根源不在 Java 代码里。
为什么不能靠调大连接池解决?
- 连接池扩容只是把压力从应用侧转移到数据库侧
- MySQL 的线程数上限由
max_connections控制,而真正瓶颈是内存和 CPU 被sp_head和外部调用吃光 - 即便你把 HikariCP 的
maximumPoolSize从 20 改成 200,MySQL 仍会在 30 QPS 下因堆内存耗尽开始拒绝新连接(报错ERROR 1040 (HY000): Too many connections,但实际Threads_connected可能才 80) - 外部接口超时(比如 30 秒)会让单个触发器拖住整条事务链,比慢 SQL 更难定位
如何快速验证和止损?
- 查触发器数量:
SELECT COUNT(*) FROM information_schema.TRIGGERS;—— 超过 50 就该警惕,>200 基本要重构 - 查内存热点:
SELECT event_name, current_alloc FROM sys.memory_global_by_current_bytes WHERE event_name LIKE '%sp_head%' ORDER BY current_alloc DESC;—— 若 >300MiB,问题大概率在此 - 紧急止血:在
my.cnf加table_open_cache_instances = 1并重启 MySQL,可降sp_head内存 90%+ - 彻底修复:把触发器里的外部调用全部移出数据库,改用应用层异步消息(如 Kafka + 消费者服务)或定时任务补偿
真正危险的不是“用了触发器”,而是把触发器当胶水层去串服务。它本该只做数据一致性校验或字段派生,而不是承担 RPC 职责。










