应优先观察Threads_created和Threads_cached趋势:若前者每秒增>1且后者长期为0,说明thread_cache_size过小或连接未释放;短连接建议设4–16,长连接可设0–4,混合场景按1小时峰值增量的1.2–1.5倍设定。

查当前连接数与Thread Cache命中情况
直接看 Threads_created 和 Threads_cached 的变化趋势,比单纯看 thread_cache_size 配置更有意义。前者增长快,说明缓存没起作用;后者长期为 0,大概率是 thread_cache_size 设得太小,或连接持续活跃没释放。
执行:
SHOW STATUS LIKE 'Threads%';重点关注三项:
-
Threads_connected:当前打开的连接数(不是活跃查询数) -
Threads_running:当前正在执行语句的线程数(反映真实并发压力) -
Threads_created:自启动以来新建线程总数(飙升=缓存失效或连接风暴)
如果 Threads_created 每秒增加 >1,而 Threads_cached 基本不动,说明连接进来后很快被销毁,没进缓存池——常见于短连接应用未配 wait_timeout 或客户端未复用连接。
识别SQL执行时的内存分配来源
MySQL 中一条 SQL 执行时用的内存,分两块:全局固定部分(如 innodb_buffer_pool_size)和会话级动态分配部分(如 sort_buffer_size)。真正导致 OOM 或内存抖动的,往往是后者在高并发下被放大。
关键参数组合有:
-
sort_buffer_size:每个排序操作独占,哪怕只排几行也按此大小分配(不复用) -
join_buffer_size:无索引 join 时使用,同样 per-connection 分配 -
read_rnd_buffer_size:随机读取结果集时使用,容易被ORDER BY+LIMIT触发 -
tmp_table_size和max_heap_table_size:控制内存临时表上限,超限自动落磁盘(Created_tmp_disk_tables上升)
执行 SHOW STATUS LIKE 'Created_tmp%'; 可判断是否频繁创建磁盘临时表;若 Created_tmp_disk_tables / Created_tmp_tables > 0.1,说明内存临时表设置偏小或 SQL 写法触发了非预期的临时表。
结合 processlist 看 state 与内存行为关联
SHOW PROCESSLIST 的 State 列不是装饰,它直接对应内存动作。例如:
-
Sorting result→ 正在用sort_buffer_size排序,若耗时长且Sort_merge_passes高,说明 buffer 不够 -
Creating tmp table→ 正在分配内存临时表,观察是否紧跟着变成converting heap to ondisk -
Copying to tmp table→ 大量数据正写入临时表,此时tmp_table_size已不够 -
Sending data→ 不代表快结束了,可能是大结果集正在从引擎层拷贝到网络缓冲区,受net_buffer_length和发送节奏影响
注意:State 是瞬时快照,需配合 Time 列看持续时间。一个 Sorting result 卡住 5 秒,比 100 个 Sleep 更值得干预。
thread_cache_size 设置的实用边界
不要按“CPU核数×2”或“内存GB数×8”这种模糊口诀设 thread_cache_size。真实有效值取决于你的连接模式:
- 短连接(如 PHP-FPM 默认):建议设为
4 ~ 16,再高基本无效,因为连接进来就走,缓存来不及热起来 - 长连接(如 Java 应用用 HikariCP):可设为
0 ~ 4,连接复用率高,Threads_created增速极慢,设太大反而浪费内存 - 混合场景:观察 1 小时内
Threads_created峰值增量,设为该值的 1.2~1.5 倍较稳妥
另外,thread_cache_size 不影响已建立连接的内存占用,它只控制空闲线程保留策略。真正吃内存的是每个连接的 sort_buffer_size 等参数乘以 max_connections —— 这个乘积才是内存爆炸的主因。


















