innodb_buffer_pool_size建议设为物理内存的50%–75%,但需确保OS可用内存≥2GB;若共存Redis等服务则需进一步下调,并配合合理设置max_connections、sort_buffer_size等per-connection参数及关闭query_cache。

buffer_pool_size 设多大才不炸内存
MySQL 的 innodb_buffer_pool_size 是内存大户,设得太大,OS 会杀进程;设得太小,磁盘狂刷,QPS 直接掉。它不是“越大越好”,而是要给 OS 和其他 MySQL 组件(比如排序、连接线程栈)留足余量。
实操建议:
- 生产环境一般设为物理内存的 50%–75%,但必须确保
free -h显示的可用内存(不含 cache/buffer)仍 ≥ 2GB - 如果机器还跑 Redis、Java 应用等,得再往下压,比如 48GB 内存的服务器,
innodb_buffer_pool_size = 24G可能就已激进 - 别直接写
24G,用字节值(如25769803776)或带单位的字符串(24G)均可,但确认 MySQL 启动日志里打印的实际值是否匹配:InnoDB: Using buffer pool size of 25.000GiB - 动态调整(MySQL 5.7+)可执行
SET GLOBAL innodb_buffer_pool_size = 25769803776,但注意:该操作会触发 buffer pool 重建,期间新请求可能短暂变慢,且不能超过innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances的整数倍限制
连接数爆满导致内存雪崩
max_connections 看似只是连接上限,但它背后每个连接默认分配约 2–8MB 内存(含线程栈、sort buffer、join buffer 等),1000 个空闲连接就能吃掉 4GB+。很多“内存高”其实不是 buffer pool 撑的,是连接池没管住。
实操建议:
- 先查真实并发:
SHOW STATUS LIKE 'Threads_connected',对比max_connections,如果长期 > 70%,说明配置偏紧;如果Threads_connected常驻 200 但max_connections=1000,那大概率是应用端没复用连接,或者连接泄漏 - 调低
max_connections到合理值(比如 300),同时在应用层强制使用连接池(如 HikariCP 的maximumPoolSize=200),并开启wait_timeout(建议 60–300 秒)自动回收空闲连接 - 警惕
sort_buffer_size和read_rnd_buffer_size这类 per-connection 参数——它们不是全局共享的,每个连接都会独立分配,设成 4M 就意味着 500 个连接吃掉 2GB,建议保持默认(256K/256K)或最多调到 1M
为什么 show processlist 看不到连接却还在吃内存
有些连接状态是 Sleep,但没及时断开;还有些是 Query 卡在锁等待、磁盘 IO 或大结果集传输中,线程没释放,内存就一直挂着。更隐蔽的是 prelocked mode 或长事务未提交,导致 undo log 和 lock 结构持续占内存。
实操建议:
- 运行
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE TIME > 60,重点看TIME > 60且COMMAND != 'Sleep'的查询,尤其是STATE为Sending data、Sorting result、Locked的 - 检查长事务:
SELECT * FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED LIMIT 5,TRX_STARTED 超过 5 分钟的,基本可以 kill(先确认业务影响) - 用
pt-pmp或perf top -p $(pgrep mysqld)看内核态热点,如果大量时间花在malloc/memcpy,说明可能是小对象频繁分配(如短连接风暴),不是 buffer pool 问题
调整后内存还是下不来?盯紧这三处
buffer_pool 和连接数调完,内存占用纹丝不动,大概率是其他模块在“静默吃内存”。这些地方不常被监控,但上线后容易失控。
实操建议:
-
table_open_cache:每张打开的表缓存一个TABLE对象,大小约 1–2KB,如果设成 10000,光这部分就吃掉 10MB+;更麻烦的是它和open_files_limit强绑定,超限会导致反复 open/close 表,CPU 和内存双高。建议设为SHOW GLOBAL STATUS LIKE 'Open_tables'峰值的 1.5 倍,上限 4096 -
query_cache_size:MySQL 8.0 已移除,但 5.7 仍默认开启(query_cache_type=1)。它对写多读少场景完全是负优化,且缓存失效时会锁全局 mutex,引发严重争用。建议直接关:SET GLOBAL query_cache_type = 0,并把query_cache_size设为 0 - 临时表:检查
Created_tmp_disk_tables / Created_tmp_tables比值,若 > 20%,说明 sort/group by 频繁落盘,此时tmp_table_size和max_heap_table_size(两者需一致)可从默认的 16M 提到 64M,但注意:每个连接都可能建临时表,别设太高
buffer pool 和连接池参数只是入口,真正卡住内存的,往往是那些没人盯着的 per-connection 缓冲区和缓存开关。改完记得看 SHOW ENGINE INNODB STATUS\G 里的 BUFFER POOL AND MEMORY 小节,以及 ps aux --sort=-%mem | head -10 确认 mysqld 进程 RSS 是否真实下降。










