MySQL 8.0 高并发下假死或OOM的主因是系统级限制未同步:ulimit -n 软限1024导致max_connections被自动压至214;需同时调systemd LimitNOFILE、配置connection_memory_limit防单连接内存溢出,并优化wait_timeout与字符集校验开销。

直接设 max_connections 不管用,不配系统限制、不调内存参数、不查真实连接状态,MySQL 8.0 很快就会在高并发下假死或 OOM 崩溃。
为什么改了 max_connections 还是卡在 214?
这不是 MySQL 没读配置,是 systemd 和内核联手截胡了。MySQL 启动时检查 ulimit -n,发现 soft limit 只有 1024(常见默认值),就自动把 max_connections 往下压到 214——因为每个连接至少要占 1 个文件描述符,还得留点余量给日志、临时表等。
- 查当前生效的限制:
cat /proc/$(pgrep mysqld)/limits | grep "Max open files" - 确认 MySQL 实际加载哪个配置:
mysql --help | grep "Default options"或SELECT @@global.config_file; - 必须在真正被读取的
my.cnf的[mysqld]段里写:max_connections = 500,别写错段落 - systemd 环境下,还要建
/etc/systemd/system/mysqld.service.d/override.conf,加:[Service] LimitNOFILE=65535 LimitNPROC=65535
然后执行systemctl daemon-reload && systemctl restart mysqld
connection_memory_limit 是 MySQL 8.0.28+ 的救命稻草
以前只能靠应用层限流或 SQL 审计防大查询吃光内存,现在服务端能直接掐单连接的内存喉咙。它不阻止连接建立,但会在线程执行过程中实时监控内存使用,超限就断开并报错 ER_CONNECTION_MEMORY_EXCEEDED。
- 启用前先开追踪:
SET GLOBAL global_connection_memory_tracking = ON; - 设单连接硬上限(比如 256MB):
SET GLOBAL connection_memory_limit = 268435456; - 注意:该限制只对新建立的连接生效;已存在的连接不会被 retroactively 限制
- 配合
SHOW STATUS LIKE 'Global_connection_memory';观察实际内存消耗趋势
别只盯 max_connections,Threads_running 和 wait_timeout 才决定是否真卡住
连上 1000 个连接,990 个是 Sleep 状态,Threads_running 长期只有 3~5,那问题根本不在上限,而在连接池没回收、应用没关连接、或者 wait_timeout 太大导致空闲连接赖着不走。
- 查真实活跃压力:
SHOW GLOBAL STATUS LIKE 'Threads_running';(正在执行 SQL 的线程数) - 查历史峰值连接数:
SHOW GLOBAL STATUS LIKE 'Max_used_connections';,比值长期 > 0.85 才需扩容 - 收紧空闲连接生命周期:
wait_timeout = 60和interactive_timeout = 60(单位秒),写进[mysqld]段 - 搭配
thread_cache_size = 8(建议设为Max_used_connections的 1/4~1/2),减少线程创建销毁开销
最易被忽略的一点:MySQL 8.0 默认字符集是 utf8mb4_0900_ai_ci,带校验规则的排序和比较比旧校验集多耗 15%~20% 内存;如果业务大量用 ORDER BY 或 GROUP BY 字符串字段,这个开销会叠加到每个连接的内存占用里——你算的 1MB/连接,实际可能奔着 1.3MB 去了。


















