做后端开发和运维的朋友,多少都会碰到过这么一个直击灵魂的问题:MySQL 到底最多能有多少连接?说实话,我刚入行时也问过类似的,得到的回答五花八门——有人斩钉截铁地说就是151,有人说是几千,还有人直接甩一句“看你机器配置”。这些答案都对,但都不完整。因为“连接数”这个问题,表面上是一个参数值,实际上牵扯到内存模型、操作系统限制、业务并发特征、连接池配置等一串连环扣。今天这篇文章,我就把这个老问题从头到尾拆开讲透,内容覆盖参数默认值、容量估算、隐藏限制、报错排查和长期监控,希望对被“Too many connections”折磨过的同学有点用。
1. 先把问题拆开:三层的“连接数”不是一回事
1.1 参数层面的 max_connections
打开 MySQL 配置文件,max_connections是所有人都认识的那个数字。它是 MySQL 服务端允许同时建立的最大客户端连接数,一旦超过这个值,新连接会被直接拒绝,客户端拿到的是1040 Too many connections错误。很多教程告诉你默认值是 151,这个数字在 MySQL 5.6、5.7、8.0 里基本一致,实际上 151 这个值的历史来源是老版本在低配机器上的保守估算,放到今天动辄几十核、上百 G 内存的服务器上,它早就不是资源瓶颈,而是业务瓶颈。
1.2 资源层面的真实上限
参数归参数,物理资源说了才算。每个连接在 MySQL 内部都会占用内存,对齐到线程栈、排序缓冲、连接缓冲、结果集缓冲这些地方。再加上 InnoDB 缓冲池、全局缓存、操作系统本身的开销,一台机器能撑起的连接数是有明确上限的。这也是为什么很多人把max_connections调到 5000 甚至更高,结果重启数据库后内存直接被打爆——连接数不是拍脑袋设的,是算出来的。
1.3 业务层面的合理容量
还有一层最容易被忽略:你的业务真的需要那么多并发连接吗?一个典型的误区是,应用层连接池的maximumPoolSize设置得很大,比如每个实例 50,部署 20 个实例就声称需要 1000 个连接。但连接池里的连接大部分时间是在池里睡大觉,真正同时执行 SQL 的可能只有几个。把连接数做上去不难,难的是让这些连接高效地“转起来”。所以我的经验是,先把参数层和资源层搞清楚,再回头审视业务层,往往能省下一大笔硬件预算。
2. 关键参数查看与配置实操
2.1 查看当前连接状态的完整命令组合
要回答“MySQL 最多能有多少连接”,第一步不是改配置,而是先看现状。我惯用下面这几条 SQL 组合,能快速拿到全景:
-- 当前配置上限 SHOW VARIABLES LIKE 'max_connections'; -- 历史最高已用连接数,这数字很能说明问题 SHOW GLOBAL STATUS LIKE 'Max_used_connections'; -- 当前活跃连接数 SHOW STATUS LIKE 'Threads_connected'; -- 当前正在执行的线程数 SHOW STATUS LIKE 'Threads_running'; -- 连接被拒绝次数 SHOW GLOBAL STATUS LIKE 'Connection_errors_max_connections';其中Max_used_connections是判断配置是否合理最核心的指标。如果它长期贴着max_connections跑,说明上限设置离真实需求太近,随时有爆的风险。如果它只有上限的一半甚至更低,那说明配置还有余量,但也要看业务的峰值波动。另外,Threads_running这个值比Threads_connected更能反映真实的并发压力——MySQL 内部同一时刻真正在执行查询的线程远小于连接数,大部分连接都处于 SLEEP 等待状态。
2.2 默认值与版本差异对比
先给一张基础对照表,方便大家在不同环境里快速对号入座:
| 版本 | 默认 max_connections | 单连接最小内存开销(约) |
|---|---|---|
| MySQL 5.5 | 151 | 约 1MB |
| MySQL 5.6 | 151 | 约 1.5MB |
| MySQL 5.7 | 151 | 约 1.5-2MB |
| MySQL 8.0 | 151 | 约 2-4MB |
为什么要强调“约”?因为每个连接的内存开销不是固定的,它受一堆参数影响:sort_buffer_size、join_buffer_size、read_buffer_size、read_rnd_buffer_size、net_buffer_length、thread_stack等等。这些参数里有些是“连接建立时按值分配”,有些是“按需动态分配”,后者的实际占用不一定等于配置值,但配置值决定了峰值上限。所以做容量规划时,我习惯按单连接 2-4MB 来估算,宁可算多,不可算少。
2.3 修改配置的正确姿势
修改max_connections有两种方式。一种是直接执行 SQL,立即生效但重启后丢失:
SET GLOBAL max_connections = 1000;另一种是改配置文件my.cnf或my.ini,在[mysqld]段下写入:
[mysqld] max_connections = 1000然后重启实例生效。线上环境我的习惯是:先用SET GLOBAL热调整,观察一段时间稳定后再落实配置文件。这里有个坑提醒一下——把max_connections调大之后,MySQL 不会主动回收之前被拒绝的等待连接,已经报错的应用端需要自行重连。另外,在部分云厂商的托管数据库上,max_connections的最小值会与实例规格绑定,如果设置低于规格默认值,会直接报错或自动重置,这点要特别留意。
3. 内存模型与连接数的容量计算
3.1 单条连接到底吃掉多少内存
要算清楚最大连接数,先得知道一个连接吃多少内存。MySQL 8.0 里,一条连接在内核侧会分配thread_stack(默认 256KB),还会准备一组会话级缓冲区,常见参数的默认值大致如下:
| 参数 | 默认值(8.0) | 分配时机 |
|---|---|---|
| sort_buffer_size | 256KB | 排序时分配,连接建立时预留 |
| join_buffer_size | 256KB | 关联查询时分配 |
| read_buffer_size | 128KB | 顺序扫描时分配 |
| read_rnd_buffer_size | 256KB | 随机读时分配 |
| net_buffer_length | 16KB | 连接建立时分配 |
| thread_stack | 256KB | 连接建立时分配 |
注意,sort_buffer_size这类参数在实际查询发生前并不会真正占满内存,但 MySQL 的优化器会按参数值来做内存规划,高并发大查询场景下,每个连接都可能把缓冲区吃到上限。所以做容量规划时,不能只看业务平均情况,要按最坏情况算——这也是为什么我前面建议按 2-4MB 每连接来估算。如果线上有大查询、批量任务,单连接峰值占用到几十 MB 也不奇怪,这种场景下优先控制慢查询和长事务,比单纯调连接数有意义得多。
3.2 一套可落地的估算公式
容量规划我一般这么算:先确定 InnoDB 缓冲池大小,这通常是 MySQL 内存开销的大头,建议占服务器物理内存的 60%-75%。然后预留操作系统和 MySQL 全局结构的内存,约 20%-25%。剩下的才是连接内存的池子。
举个例子,一台 16GB 内存的服务器,InnoDB buffer pool 设 10GB,系统及其他开销预留 3GB,留给连接的内存大约还有 3GB。按单连接 3MB 估算,理论最大连接数约 1000。所以:
可用连接内存 = 物理内存 - innodb_buffer_pool_size - 系统预留内存 理论最大连接数 = 可用连接内存 / 单连接预估内存这个公式算出来的是“物理上限”,不是“推荐值”。推荐值我一般再打个 7-8 折,留出余量给突发流量和内存抖动。另外必须强调,千万不要为了堆连接数把innodb_buffer_pool_size调小,缓冲池缩水带来的性能损失,十个连接数都补不回来。连接内存是“潜在占用”,缓冲池是“恒定收益”,两害相权,宁缺连接,不缺缓存。
3.3 压测验证与动态调整
算完理论值,一定要用压测验证,不能只看公式。我常用的方法是用sysbench或mysqlslap模拟不同并发数,同时监控两个关键指标:内存使用率和连接成功率。
mysqlslap --concurrency=200,400,800 --iterations=3 \ --number-of-queries=10000 \ --query="SELECT * FROM test_table WHERE id=1" \ -u root -p压测时观察内存曲线:如果内存长期高位、SWAP 开始增长,说明并发数已经触顶;如果内存稳定、连接全部成功、Threads_running远小于Threads_connected,说明还有上探空间。实测里我碰到过一台 32GB 内存的物理机,理论算出来能开 3000 连接,但压测到 2500 时系统内存已经吃紧,原因是业务侧某些查询触发了大量排序和临时表分配,单连接内存远超估算均值。这种问题就不是调参数能解决的,得从慢查询和 SQL 执行计划入手。所以算完参数,一定要做压测对照,数据和经验互相印证才算数。
4. 那些容易被忽略的隐性限制
4.1 操作系统文件句柄限制
很多人把max_connections调上去了,重启数据库,结果发现连接数到了几百就上不去了,连接直接失败,日志里全是Can't create a new thread或者Resource temporarily unavailable。这种情况十有八九是操作系统的文件句柄限制在捣鬼。
MySQL 在 Linux 上每个连接都要占用一个文件描述符,而操作系统对单进程能打开的文件描述符数有默认限制,通常是 1024 或 65535。查看和修改方式如下:
# 查看当前限制(软限制和硬限制) ulimit -n # 修改用户级限制,追加到 /etc/security/limits.conf mysql soft nofile 65535 mysql hard nofile 65535改完文件后重新登录或重启 MySQL 进程生效。另外,systemd管理 MySQL 时还要处理LimitNOFILE参数,否则ulimit改了不生效。这个坑我踩过不止一次——配置文件的限制改了,systemd 里的LimitNOFILE没同步,连接数卡在半路,排查了半天。
4.2 线程缓存与连接建立成本
MySQL 为每个连接分配一个线程,频繁创建和销毁线程是有开销的。thread_cache_size参数控制缓存线程的数量,默认值是-1(自动调整,约等于 8 倍 CPU 核心数),范围 0 到 16384。连接断开后,线程不会被立刻销毁,而是放进缓存等待复用,这能显著降低新建连接的成本。
【注意】thread_cache_size不是连接上限,也不是越大越好。设太大只是多占用一些内存,对高并发下的连接建立速度提升有限,因为线程复用的命中率在高峰期接近饱和。我一般建议保持自动值,或手动设为 64-128。查看命中率用一个状态量:Threads_created和Connections。如果Threads_created / Connections比例过高,说明线程反复新建,此时才值得调大thread_cache_size。
4.3 超时参数与连接池的配合
还有一个隐性杀手是超时参数。wait_timeout默认 28800 秒(8 小时),意味着一条空闲连接可以挂在这里睡 8 小时才被服务端断开。如果你的应用层连接池没做好空闲回收,或者业务代码里存在连接泄漏,大量 SLEEP 连接会把连接池占满,新请求进来直接撞上连接数上限。
我的经验是:OLTP 系统的wait_timeout设置在 60-300 秒比较合理,交互式连接用interactive_timeout单独控制。同时,应用侧连接池的空闲连接超时要与服务端匹配,比如服务端wait_timeout=120,连接池的idleTimeout就设在 60 秒左右,保证池里的连接不会被服务端悄悄断掉。连接池还要配置连接存活校验(比如连接池的testOnBorrow或validationQuery),否则服务端断连后,池里还拿着失效连接用,就会出现“连接建立成功但执行 SQL 时报连接已关闭”的诡异现象。
4.4 网络层与反向代理的队列限制
最后别忘了网络层的因素。Linux 的 TCP 全连接队列长度由back_log参数控制,MySQL 默认值是-1(自动,约等于max_connections的一半)。并发请求瞬间拥入时,如果back_log太小,内核会直接丢弃 SYN 包,客户端表现为“连接超时”,数据库日志里却干干净净,没有报错。这个参数可以从 128、256 往上调,但要结合网络环境,不是越大越好,同时还要看是否在反向代理或负载均衡层被限制了连接队列。线上出现过一次很典型的案例:数据库连接数远没到max_connections,但高峰期客户端大面积报连接超时,查到最后是负载均衡层的连接队列满了,跟 MySQL 本身半毛钱关系没有。
5. 实战:从“Too many connections”到根因定位
5.1 报错出现时的第一反应
看到Too many connections,第一反应不是去把max_connections拉高,而是先搞清楚连接被谁占满了。我遇到过最经典的一次:某业务高峰期突然报错,监控面板显示max_connections是 500,Threads_connected已经顶满 500,但Threads_running只有 20。也就是说,480 条连接都在 SLEEP,没有干正事。
5.2 完整排查步骤实录
排查顺序我整理成一套固定动作,遇到类似情况可以直接抄:
第一步,用带admin权限的账号登录(普通账号可能登录不了,因为连接已满,MySQL 为管理账号保留了额外的连接通道):
-- 查看所有连接的分布 SELECT id, user, host, db, command, time, state, info FROM information_schema.processlist ORDER BY time DESC; -- 按来源 IP 分组统计连接数 SELECT host, COUNT(*) AS cnt FROM information_schema.processlist GROUP BY host ORDER BY cnt DESC;第二步,定位僵尸连接。如果看到大量Command=Sleep、Time很大的连接,基本可以判断是应用侧连接泄漏,或者连接池配置不当。此时可以临时杀掉这些空闲连接止损:
-- 杀掉某条连接 KILL 12345; -- 批量杀掉空闲超过 600 秒的 sleep 连接 SELECT CONCAT('KILL ', id, ';') FROM information_schema.processlist WHERE command = 'Sleep' AND time > 600;第三步,检查应用连接池。那次案例最终发现是应用侧连接池的maximumPoolSize设置过大,20 个服务实例每个池子上限 100,理论上最大能开 2000 条,远超数据库的 500,高峰期自然把库打满。调整方法很直接:数据库max_connections按物理容量调到 800,应用连接池单实例上限降到 30,同时开启空闲连接回收。调整后,连接数峰值稳定在 400 左右,Threads_running也保持在个位数,问题彻底消失。
5.3 长期监控与容量规划
事后一定要补充监控。我建议至少盯这几个指标:Threads_connected、Threads_running、Max_used_connections、Connection_errors_max_connections。可以把监控告警设置在Threads_connected达到max_connections的 80% 时触发,预留反应时间。
容量规划方面,形成一套“需求-容量-余量”的对照习惯。业务并发需求来了,先算应用侧需要多少个连接(实例数 × 单实例池子上限 × 池子利用率),再对照数据库物理容量,两者之间留出 20%-30% 余量。连接数扩容要和 SQL 性能优化同步做——如果一条查询跑 3 秒,100 个连接只能支撑每秒 33 个查询;同样的查询优化到 100ms,同样 100 个连接就能支撑每秒 1000 个查询,连接数不但不用增,还能降下来。很多“连接不够用”的假象,本质是慢查询占着茅坑不拉屎。
6. 常见问题速查表
把平时群里问得最多的问题整理成一份速查表,方便直接对照处理:
| 问题现象 | 可能原因 | 排查/解决方案 |
|---|---|---|
| 报 1040 Too many connections | 连接数达到 max_connections 上限 | 查看 Threads_connected 与 processlist,识别并清理 SLEEP 堆积;按内存算出合理上限后扩容 |
| 连接数远未到上限但连接超时 | TCP 队列满、反向代理限制、网络抖动 | 检查 back_log、负载均衡连接队列、网络丢包 |
| 调大 max_connections 后内存飙升 | 单连接内存估算不足,缓冲池被挤占 | 按单连接 3-4MB 重算,恢复 innodb_buffer_pool_size,限制大查询 |
| 修改配置文件后不生效 | 配置错位置、未重启、systemd 限制 | 确认写在 [mysqld] 段,重启后用 SHOW VARIABLES 验证 |
| 大量 Sleep 连接堆积 | 连接池泄漏、wait_timeout 过长 | 调整 wait_timeout,连接池开启空闲回收与校验 |
| 连接数只在高峰期爆 | 应用池子没限流,突增流量冲击 | 应用侧限流、缩小单实例连接池上限、增加只读实例分担 |
这份表里每一项都是我实际碰到过的问题,建议把前两行当成必查项。很多“数据库连接数不够”的事故,最后定位下来都是应用层问题,数据库只是背锅的那个。反过来,如果你把max_connections当成万能药,逢堵就调大,那迟早会在内存上栽跟头。正确的心态是:连接数只是水管的口径,真正决定流量的还是水管里的水压——也就是 SQL 的质量、索引的设计和事务的粒度。
我个人的体会是,“MySQL 最多能有多少连接”这个问题,答案不是一个死数字,而是由你的机器内存、InnoDB 缓冲池配置、业务并发模型、应用连接池策略乃至操作系统参数共同决定的。与其四处打听“标准答案”,不如把今天这套方法跑一遍:先看现状、再算容量、压测验证、监控兜底。等你真正做过一轮,下次再有人问这个问题,你就能告诉他:能开多少不是 MySQL 说的算,是你自己的架构和代码说的算。最后再分享一个小技巧——每次调整完连接数,记得把调整前后的Max_used_connections记录到变更文档里,下次出现同类问题时,这些历史数据就是你最有力的判断依据。