本系列:《高性能MySQL第三版》源码级伴读
实验环境:MySQL 8.0.46-debug
本文是 Phase 1 第 2 篇:连接层
一、场景:Too many connections 但 CPU 很低
❓ 场景:凌晨告警 ERROR 1040 (HY000): Too many connections,但top看 CPU 只有 30%。
为什么 CPU 不高,连接却爆了?因为 idle 连接在吃内存,不在吃 CPU。
我们这一篇就解决两个问题:
一个 idle 连接到底吃多少内存? max_connections到底该怎么设?
二、书上怎么说?——《高性能MySQL》关于连接管理的观点
《高性能MySQL第三版》在第3章关于"服务器设置"中明确指出:
「MySQL 默认采用 one-thread-per-connection 模型,每个连接对应一个线程。」
书里也提到几个关键点:
连接是有成本的(线程栈、内存等) thread_cache_size影响新建连接的性能 wait_timeout决定空闲连接的存活时间
但书没量化的是:
❌ 一个连接真实吃多少内存(栈、THD结构、buffer等) ❌ 多少连接是安全的(不是看 max_connections,是看 RAM 上限) ❌ 线程缓存真正能省多少 ms
三、源码:MySQL 8.0 的连接模型
MySQL 8.0 默认是 one-thread-per-connection。源码入口在:
"color: #6a737d; font-style: italic;">// sql/conn_handler/connection_handler_per_thread.cc:246staticvoid *handle_connection(void *arg) {"color: #6a737d; font-style: italic;">// 每个新连接分配一个线程,入口函数"color: #6a737d; font-style: italic;">// 内部调用 do_command() 循环处理客户端请求 }"color: #6a737d; font-style: italic;">// sql/conn_handler/connection_handler_manager.cc:65"color: #6a737d; font-style: italic;">// 枚举:SCHEDULER_ONE_THREAD_PER_CONNECTION(默认)
关键结构:
THD(Thread Handler Descriptor):每个连接对应一个,存储会话状态、解析树等 Per_thread_connection_handler(connection_handler_impl.h:42):实际管理线程创建 thread_cache:线程复用,避免反复创建/销毁
内存开销估算(一个 idle 连接):
THD 结构本身:~50-100KB 线程栈( DEFAULT_THREAD_STACK=1MB,见 include/my_thread.h:55):1MBnet_buffer_length(默认 16KB):16KB 各种 cache( sort_buffer,join_buffer等):只在需要时分配- 总计:一个 idle 连接约 1-2MB
(线程栈占大头)
四、实测:观测我们的实例
-- 查看当前连接配置SHOWVARIABLESWHERE Variable_nameIN ('max_connections','thread_cache_size','wait_timeout','max_connect_errors','thread_handling');-- 实验输出(在MySQL8.0.46-debug实例上实测):-- +--------------------+---------------------------+-- | Variable_name | Value |-- +--------------------+---------------------------+-- | max_connect_errors |100 |-- | max_connections |151 |-- | thread_cache_size |9 |-- | thread_handling | one-thread-per-connection |-- | wait_timeout |28800 |-- +--------------------+---------------------------+-- 查看连接实时状态SHOWSTATUSWHERE Variable_nameIN ('Threads_connected','Threads_created','Threads_cached','Threads_running','Max_used_connections');-- 实验输出(在MySQL8.0.46-debug实例上实测):-- +----------------------+-------+-- | Variable_name | Value |-- +----------------------+-------+-- | Max_used_connections |1 | ← 历史峰值-- | Threads_cached |7 | ← 当前缓存中的线程-- | Threads_connected |1 | ← 当前连接数-- | Threads_created |8 | ← 累计创建过的线程-- | Threads_running |2 | ← 当前正在运行的线程-- +----------------------+-------+-- 当前 mysqld 进程内存-- +------+----------+------------+----------------------------------+-- | PID | RSS(MB) | VSZ(MB) | CMD |-- +------+----------+------------+----------------------------------+-- |2671620 |447 |3348 | mysqld --basedir=/usr/local/mysql|-- +------+----------+------------+----------------------------------+
解读:
当前只用了 447MB 内存(Buffer Pool 默认 128MB + 基础开销) 如果把 max_connections设为 1000,并且真的有 1000 个 idle 连接:
- 1000 × 1MB(仅线程栈)= 1GB 仅连接开销
- 每个连接如果再用了 sort_buffer / join_buffer(各 256KB),瞬间就吃 1.5GB+
五、实战启示:max_connections 该怎么设?
1. 反推公式
最大可设置 = (可用RAM - MySQL自身开销 - Buffer Pool) / 每连接最大内存举例:服务器 64GB,Buffer Pool 32GB,系统预留 4GB,MySQL其他开销 2GB:
可用 = 64 - 32 - 4 - 2 = 26GB 每连接按 4MB 算:26000/4 = 6500 实际生产建议:max_connections = 2000-30002. 连接风暴怎么防?
--1. 缩短短闲连接存活时间(默认8小时太长)SETGLOBAL wait_timeout =300;-- 5分钟--2. 调大线程缓存SETGLOBAL thread_cache_size =100;-- 减少新线程创建--3. 监控 Threads_created 增长SHOWSTATUSLIKE'Threads_created';-- 如果这个值增长很快 → 线程缓存不够--4. 看 Max_used_connectionsSHOWSTATUSLIKE'Max_used_connections';-- 这是历史峰值,应该 < max_connections 的80%
3. 应用层用连接池
永远不要让应用直连 MySQL。连接池(HikariCP、Druid)保持 10-50 个连接,应对 1000 个并发请求。
六、本篇对应解决的运维问题
Too many connections | Max_used_connections 是否真的满了,再看 RAM |
thread_cache_size | |
wait_timeout |
七、关键参数速查
max_connections | |||
thread_cache_size | Threads_created 增长 | ||
wait_timeout | |||
max_connect_errors |
八、本篇作业 & 下期预告
作业
执行 SHOW VARIABLES LIKE 'max_connections';看你的设置算一下你机器的 max_connections合理上限(用第 5 节的公式)用 ps -o rss -p $(pgrep mysqld)看当前 RSS,按公式反推"还能再开多少连接"
下期预告
Phase 1 第 3 篇|SQL 解析:你的 SQL 是怎么变成语法树的——连接建立后,SQL 文本就送到解析器。我们会看到 Yacc/Bison 生成的解析器干了什么,以及预编译语句为什么快(用真实验证)。
如果觉得有帮助,请点赞、在看、转发三连。下一站,我们进入解析器。
⚖️ 版权声明:
本文为原创技术分析文章,非官方授权翻译或转载 对《高性能MySQL第三版》的引用仅限简短概括,用于评论和教学,符合《著作权法》第24条合理使用 MySQL 源码引用遵循 GPL v2 开源协议,用于教育目的的代码片段分析不构成"分发" "MySQL"为 Oracle Corporation 注册商标,本文使用仅为技术描述 建议购买原书深入学习:《高性能MySQL》第三版,电子工业出版社 未经作者许可,禁止转载或用于商业用途
夜雨聆风