真正危险的是“安静但吃内存”的长连接,需用performance_schema查单连接内存占用、设connection_memory_limit熔断、调用mysql_reset_connection重置缓冲区,并禁用overcommit防oom。

单靠SHOW PROCESSLIST或连接数监控,根本发现不了正在爬升的OOM风险——真正危险的是那些“安静但吃内存”的长连接。
查真实内存占用:别只看Threads_connected
连接数稳定不等于内存安全。MySQL 8.0.28+ 的 performance_schema 才能定位到具体连接在吃多少内存:
- 先开启追踪:
SET GLOBAL global_connection_memory_tracking = 1;(需 SUPER 权限) - 查全局内存水位:
SHOW GLOBAL STATUS LIKE 'Global_connection_memory';——超物理内存 70% 就该告警 - 查单个高耗连接:
SELECT THREAD_ID, USER, CURRENT_MEMORY FROM performance_schema.memory_summary_by_thread_by_event_name WHERE CURRENT_MEMORY > 1048576;(>1MB 的连接重点关注) - 注意:
innodb_buffer_pool_size不计入此统计,它单独受配置控制
设 connection_memory_limit 主动熔断
对应用账号设硬性内存上限,比调 max_connections 更治本:
- 执行:
SET PERSIST_ONLY connection_memory_limit = 2097152;(2MB) - 该限制仅对无 SUPER 权限的用户生效;
root或 DBA 账号不受影响 - 一旦某条 SQL 预估内存超限,立刻报错:
ERROR 1105 (HY000): Memory limit exceeded for this connection - 必须搭配
global_connection_memory_tracking使用,否则无法触发熔断逻辑
用 mysql_reset_connection 重置连接状态
长连接复用久了,sort_buffer_size、tmp_table_size 等会话级内存不会自动释放——mysql_reset_connection() 是 MySQL 5.7+ 提供的轻量级清理手段:
- 它不关闭 TCP 连接,只重置会话变量、释放临时表、清空排序/JOIN 缓冲区
- Java 应用可在连接归还池前调用:
connection.unwrap(com.mysql.cj.jdbc.ConnectionImpl.class).resetConnection(); - Python PyMySQL 可在
cursor.close()后显式调用conn.reset_session() - 注意:它不能替代连接轮换,只是延缓内存爬升;若已发生大事务未提交,仍需先
ROLLBACK
禁用 overcommit + 合理设 buffer pool
Linux 内核层面的保护比 MySQL 自身更底层、更关键:
- 在
/etc/sysctl.d/99-mysql.conf中设:vm.overcommit_memory=2和vm.overcommit_ratio=95,让malloc()在超限时直接失败,而不是等 OOM Killer 动手 -
innodb_buffer_pool_size别超过物理内存的 70%,留足空间给连接内存、系统缓存和突发查询 - 每个连接默认栈大小由
thread_stack控制(通常 256KB),200 个连接就占 50MB,别忽略这个固定开销 - Swap 不是救命稻草:
vm.swappiness=10+ SSD Swap 分区,仅作兜底,不依赖它扛压力
最易被忽略的一点:OOM 往往不是因为连接数多,而是某几个连接在跑没加 LIMIT 的全表 JOIN,或者在事务里持续插入百万行数据——监控要落到线程粒度,策略要覆盖“内存熔断”和“连接重置”两个动作,缺一不可。











