mysql无进程级硬内存上限,必须用cgroup v2的memory.max设硬限(如systemd memorymax=4g),并同步调低innodb_buffer_pool_size(≤可用内存×60%)、tmp_table_size与max_heap_table_size(统一设为64m)等软参数,防止oom killer杀进程。

MySQL 本身没有 memory_limit 配置项
别在 my.cnf 里找 max_memory_mb 或类似参数——MySQL 不提供进程级硬内存上限。它的内存由多个独立参数叠加构成:innodb_buffer_pool_size、sort_buffer_size、tmp_table_size 等全是“软分配”,实际运行中可能远超配置值,尤其在高并发下。
常见错误现象:mysqld 进程 VmRSS 持续上涨,最终被系统 OOM killer 杀掉,日志出现 Killed process <pid> (mysqld)</pid>。
-
innodb_buffer_pool_size是最大头号消耗者,但它只管 InnoDB 缓存,不包含线程栈、临时表、performance_schema、连接对象等 -
sort_buffer_size和join_buffer_size是 per-connection 分配的,100 个连接 × 2MB = 200MB,但默认是 256KB,调大前必须算总账 -
tmp_table_size和max_heap_table_size必须设为相等,否则隐式内存临时表会退化成磁盘表,既慢又难察觉
必须用 cgroup v2 设 memory.max 硬限制
Linux cgroup v2 是当前唯一可靠方案,能真正约束 mysqld 进程组的全部 RSS 占用(anon memory + page cache + buffers),v1 已弃用且对 MySQL 失效。
裸机部署推荐直接用 systemd,比手动操作 /sys/fs/cgroup/ 更稳妥:
- 新建配置文件:
/etc/systemd/system/mysqld.service.d/override.conf - 写入内容:
[Service] MemoryMax=4G MemorySwapMax=0
- 执行:
sudo systemctl daemon-reload && sudo systemctl restart mysqld
关键点:MemoryMax 是硬限,MemoryLimit 是 v1 兼容项,在 v2 下无效;MemorySwapMax=0 强制禁用 swap,避免内存压力下性能雪崩。
innodb_buffer_pool_size 别超过可用内存 × 60%
“设为物理内存 70%–80%”是典型误用场景。真实可用内存 = MemAvailable(不是 free -h 的 free)− 系统保留(≥1GB)− 其他常驻进程 − 容器 memory limit(如果用了 cgroup)。
- 混部或容器环境,保守取 ≤ 可用内存 × 60%
- 小内存机器(≤4GB),直接设
innodb_buffer_pool_size=1G,别碰 75% - 单位写
1G(MySQL 8.0+ 对单位敏感),别写1024M - 验证:启动后查
ps -o pid,vsz,rss,comm -C mysqld,RSS必须明显低于MemoryMax
tmp_table_size 和 max_heap_table_size 必须同步调低
云主机默认关 swap,OOM Killer 不会等,而是立刻干掉最大 RSS 进程——通常是 mysqld。大查询建隐式内存临时表时,就靠这两个参数兜底。
- 统一设为
tmp_table_size = 64M、max_heap_table_size = 64M(注意单位是M,不是MB) - 这两个值是 per-connection 的,100 并发理论上最多消耗 6.4GB,必须纳入总内存预算
- 查
SHOW GLOBAL STATUS LIKE 'Created_tmp%tables',若Created_tmp_disk_tables持续增长,说明临时表已落地磁盘——该优化 SQL,而不是加内存
最易被忽略的是:cgroup 设置后,MySQL 仍按宿主机总内存估算 buffer pool,所以必须手动调低 innodb_buffer_pool_size,否则它会在硬限内反复触发内存回收,导致抖动。











