mysql out of memory错误主因是mysqld进程rss远超预期且不释放,非单纯内存配置不足;需用ps -o pid,rss,vsz,comm -c mysqld查真实占用,重点关注json排序、临时表、connection泄漏等隐性内存消耗。

MySQL报Out of memory错误,绝大多数情况不是因为“内存配少了”,而是进程实际占用(RSS)远超预期,且持续不释放。直接调大innodb_buffer_pool_size往往加速崩溃。
怎么看 mysqld 真实内存占用?别信 SHOW VARIABLES
所有显式配置参数加起来(比如innodb_buffer_pool_size + sort_buffer_size × max_connections)只是理论下限。真实压力来自进程 RSS,它包含线程缓冲、临时表、performance_schema 开销等隐性部分。
- 立刻执行:
ps -o pid,rss,vsz,comm -C mysqld,看rss值(单位 KB) - RSS 超过物理内存 80%,或比你所有内存参数总和高出一倍以上,就说明有泄漏或失控增长
- 容器环境还要查 cgroup 限制:
dmesg -t | grep -i "killed process",确认是不是被 OOM Killer 杀的
哪些查询会偷偷吃光内存?重点盯 ORDER BY + JSON + LIMIT
MySQL 8.0.20+ 版本对 JSON 字段的排序行为变了:即使只选几列,只要排序字段附近有 JSON 列(如 specs_json),就会把整段 JSON 加载进 sort_buffer_size。而默认值常只有 256KB~1MB,几条记录就溢出。
- 典型高危 SQL:
SELECT id, name, specs_json FROM products WHERE category_id = 123 ORDER BY create_time DESC LIMIT 10000,20 - 验证方式:去掉
ORDER BY或去掉specs_json字段,看是否还报ERROR 1038 (HY001): Out of sort memory - 临时缓解:调大
sort_buffer_size(仅限单次会话):SET SESSION sort_buffer_size = 4194304;(4MB)
为什么调大 innodb_buffer_pool_size 反而更危险?
这个参数分配的是 mmap 匿名内存,Linux 内核不会优先把它换出;但 vm.swappiness 高时,内核又会疯狂尝试换出其他页,最终导致整体内存调度失衡,触发 OOM Killer 杀 mysqld。
- 小内存机器(≤4GB)别设超过物理内存的 50%:2GB 机器建议 ≤1024M,4GB 机器上限 2048M
- 混部环境(如宝塔共存 Nginx/PHP)必须同步压低
max_connections,否则每个连接独占的sort_buffer_size会指数级放大风险 - 容器部署务必检查 cgroup memory limit,
innodb_buffer_pool_size必须显著低于该 limit,留足余量给线程缓冲和临时表
怎么定位正在吃内存的活跃查询?
别只盯着慢日志——吃内存的查询可能执行很快,但中间结果极大。关键看状态和内存分配峰值。
- 查当前卡在临时表的连接:
SELECT * FROM information_schema.PROCESSLIST WHERE State IN ('Creating tmp table', 'Copying to tmp table'); - 查各线程内存分配(需开启 performance_schema):
SELECT THREAD_ID, EVENT_NAME, CURRENT_NUMBER_OF_BYTES_USED FROM performance_schema.memory_summary_by_thread_by_event_name WHERE EVENT_NAME LIKE '%memory%' AND CURRENT_NUMBER_OF_BYTES_USED > 10000000; - 开全量日志辅助分析:
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 0; SET GLOBAL log_queries_not_using_indexes = ON;,再用pt-query-digest看Rows_examined和Tmp_tables字段
最易被忽略的一点:RSS 持续缓慢上涨但无明显慢查询,大概率是 connection 泄漏或未关闭的游标,而不是配置问题。先确认 Threads_connected 是否长期高于业务均值,再查应用层连接池设置。











