触发器不占连接但会耗尽内存导致MySQL假死;高并发下反复加载sp_head结构体撑爆堆内存,使MySQL响应变慢、连接失败;需查TRIGGERS数量(>200危险)和memory/sql/sp_head::main_mem_root内存占用(>500MiB可锁定),并设table_open_cache_instances=1降内存90%+,清理冗余触发器更治本。

触发器本身不占连接,但会吃光内存导致MySQL假死
触发器不会直接占用数据库连接,所以SHOW PROCESSLIST里看不到它“卡住”连接。真正的问题是:大量触发器在高并发下反复加载sp_head结构体,把MySQL的堆内存(尤其是memory/sql/sp_head::main_mem_root)撑爆,导致MySQL响应变慢、连接建立失败、应用端连接池持续重试——最终表现为“连接池爆满”。这不是连接没还,而是MySQL已经快没力气接新连接了。
查触发器数量和内存占用必须一步到位
别猜,直接跑这两条SQL:
SELECT COUNT(*) FROM information_schema.TRIGGERS;
超过200个就危险;再查内存:
SELECT event_name, current_alloc FROM sys.memory_global_by_current_bytes ORDER BY current_alloc DESC LIMIT 5;
如果memory/sql/sp_head::main_mem_root排第一且值 >500MiB,基本锁定。注意:这需要performance_schema已开启,否则先确认SELECT @@performance_schema;返回1,没开就得加配置重启。
table_open_cache_instances=1 是最有效的止血操作
默认值16会让每个触发器在16个cache instance里各存一份sp_head,289个触发器×16 = 近4600个实例。改法很简单:
- 在
my.cnf的[mysqld]段加:table_open_cache_instances = 1 - 必须重启MySQL才生效
- 内存占用通常能降90%+,效果立竿见影
别碰thread_stack,调大它对sp_head问题毫无帮助,反而可能掩盖真实泄漏。
清理冗余触发器比调参更治本
很多触发器是历史遗留、测试用或早已失效的。执行前先备份:
SELECT CONCAT('DROP TRIGGER ', TRIGGER_SCHEMA, '.', TRIGGER_NAME, ';') FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA NOT IN ('mysql', 'sys');
人工筛选后批量清理。重点删三类:
- 同表上功能重复的多个触发器
- 只在开发/测试环境部署过、线上根本没用的
- CREATE_TIME很老(比如2年前)、且
TRIGGER_BODY里含明显调试语句的
触发器不是越全越好,是越精越稳。一个表有3个以上触发器,就要警惕设计是否合理。

















