news 2026/11/2 8:33:37

MySQL最大连接数调优:从默认100到高并发连接规划与验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL最大连接数调优:从默认100到高并发连接规划与验证

凌晨两点被告警叫醒,应用日志里躺着一行ERROR 1040 (HY000): Too many connections,服务端连接池全红,接口超时率瞬间飙到 40%——这种场面做过线上业务的人大概率都经历过。MySQL 最大连接数这个参数,改起来只是一行配置的事,难的是改之前要算什么、改之后要连带动什么,以及为什么有的人调到 16384 依然稳定,有的人调到 2000 就开始 OOM。这篇就围绕max_connections从默认 100 一路调到 16384 的完整链路,把我自己踩过的坑、算过的账、验证过的方法摊开讲一遍。不管你是刚接手数据库运维的新人,还是写业务代码时被连接池报错折磨过的后端,看完至少能搞清楚三件事:这个参数的天花板在哪、你的机器到底能吃下多少连接、改完之后怎么验证它是真的生效了而不是"看着生效"。

1. 先把"连接数"这件事想明白:max_connections 到底管的是什么

1.1 一条连接在数据库服务端不是一个抽象概念

很多人对连接的理解停留在"客户端连上来就算一条",但服务端视角完全不是这样。MySQL 采用的是单连接单线程模型(社区版,不考虑线程池插件的情况下),每接受一个客户端连接,服务端就要分配一个线程、一份线程栈、一组会话级的缓冲区结构,还要在文件描述符表里占一个位置。换句话说,max_connections表面上限制的是"同时能有多少个客户端连进来",实际上限制的是"服务端愿意为连接这件事付出多少线程、多少内存、多少个 fd"。

这里有个特别容易被忽略的细节:连接建立之后并不立刻消耗全部内存。像sort_buffer_size、join_buffer_size、read_buffer_size这些会话级缓冲区,是按需分配的——只有这条连接真的执行了需要排序、需要 JOIN、需要全表扫描的语句,对应的内存才会被吃掉。所以你按照max_connections × 所有 buffer 之和去算内存,会算出一个吓人的数字然后不敢改;反过来只按线程栈去算,又会在业务高峰期被真实的 OOM 打脸。合理的做法是取一个"单连接平均峰值内存"的经验值,下面算账那节会展开。

提示:thread_stack在 MySQL 5.7 里默认是 256KB,到 8.0 变成了 1MB。仅仅这一个参数,在 1000 连接的规模下,两代版本的内存差距就是 700MB 左右。升级版本之后重新核算连接数预算,是很多人漏掉的一步。

1.2 默认 100 的来历,以及 16384 这个数字的真实含义

早期 MySQL 的max_connections默认值确实是 100,这个数字放在二十年前单机、低并发的年代完全够用。后来官方在不同版本里把默认值往上抬过,很多发行版打包时也会做自己的调整,所以你在不同环境里SHOW VARIABLES LIKE 'max_connections'看到的可能是 100、151、214 之类的不同数字,这属于正常现象,别慌。

至于16384,它并不是 MySQL 的硬性上限。max_connections的可设置范围上限在 MySQL 8.0 里是 100000,理论上你能填一个很大的数。但在工程实践中,16384 经常被当成一个"甜点值"或者"经验天花板",原因有三层:

第一层是文件描述符。Linux 默认单进程nofile限制通常是 1024 或者 65535 两种常见档位,16384 条连接加上表文件句柄、日志文件句柄、临时文件句柄,差不多要吃掉两万个左右的 fd。这个量级在把LimitNOFILE调到 65535 之后是安全的,再往上就要考虑系统的fs.file-max全局上限了。

第二层是内存。按每连接保守估计 2MB 到 10MB 的峰值占用(取决于你的 SQL 复杂度和 buffer 配置),16384 连接的峰值内存需求在 32GB 到 160GB 之间浮动。低于 64GB 内存的机器硬调到这个数,基本等于给自己埋雷。

第三层是调度开销。线程数过万之后,操作系统的上下文切换成本会显著上升,vmstat里的cs列会给出很直观的反馈。到了这个阶段,增加连接数带来的吞吐提升已经微乎其微,纯粹是在用稳定性换一个好看的数字。

所以说,16384 不是一个"应该调到"的目标值,而是一个"在极端场景下、配套资源都到位时可以考虑的上限参考"。真正该调到多少,取决于你的业务形态和硬件底子,而不是标题里的那个数字。

2. 改之前先算账:连接数到底该设多少

2.1 从应用侧往数据库侧倒推,比拍脑袋靠谱得多

我见过太多人调这个参数的方式是:报错了,加个零,重启,完事。这种操作的后果通常是下一次报错来得更晚、更凶。比较靠谱的做法是从应用侧往下倒推。

假设后端有 8 个服务实例,每个实例用的是 HikariCP,maximumPoolSize配的是 20,那么理论上这 8 个实例最多会向数据库申请 160 条连接。这时候数据库的max_connections至少要在 160 之上留出余量,因为还有运维工具、监控采集、备份任务、从库复制线程要占坑。行业里常用的经验系数是1.2 到 1.5 倍,取 1.3 倍的话,160 × 1.3 ≈ 208,配到 250 到 300 是比较舒服的区间。

这个算法听起来简单,但坑在于你必须把所有的连接来源都算进去,不能只算主业务服务。常见的漏算项包括:定时任务服务、数据同步中间件、BI 报表查询、DBA 的客户端工具、监控 agent、连接池的健康检查连接、以及最容易被忘记的——从库的复制线程和半同步复制的额外连接。我碰到过一次连接数莫名其妙打满,最后查出来是一个早就下线但定时器还在跑的老服务,每隔五分钟建一批连接不释放,藏了三个月才被发现。

还有一个隐性放大器是连接池的配置错误。有的框架在连接池耗尽后会新建物理连接而不是等待,有的在事务回滚后连接没归还,这类问题会让数据库侧看到的连接数远超应用侧配置的理论上限。所以在调max_connections之前,先把应用侧的连接池配置和连接泄漏检查做一遍,往往比单纯加数字有效得多。

2.2 内存、文件描述符、线程三笔账要一起算

连接数不是孤立参数,它同时被三样东西制约。我习惯把这三笔账列在一张表里过一遍,缺哪笔补哪笔。

约束项计算方式常见坑点
内存max_connections × 单连接峰值内存 + 缓冲池 + 系统预留只算线程栈,漏算会话级 buffer
文件描述符max_connections × 1.2 + 表文件/日志/临时文件开销只改配置不改 systemd 的LimitNOFILE
线程数max_connections + 后台线程,对比系统的kernel.threads-max和nproc限制容器环境里 PID 上限被压得很低

内存那一笔,我一般按下面的思路估:先看innodb_buffer_pool_size占了多少,这部分是固定的;再看单连接的固定开销,主要是thread_stack加上连接结构体本身,1MB 到 1.5MB;最后看浮动开销,也就是会话级 buffer 的峰值。浮动部分最难估,我的经验是:以读为主、SQL 简单的业务,单连接浮动开销按 1MB 算;有大量排序、JOIN、临时表的分析型查询,单连接浮动开销可以到 10MB 甚至更高。

举个例子,一台 64GB 内存的机器,innodb_buffer_pool_size给了 40GB,系统和其他进程预留 8GB,剩下 16GB 给连接用。如果按单连接峰值 4MB 算,16GB / 4MB = 4096,再留 30% 的余量,实际配到 2800 到 3000 是比较稳的。你要是硬填 16384,那这台机器在业务高峰期一定会因为 swap 或者 OOM Killer 出事。

文件描述符那笔账更容易翻车。很多人在my.cnf里写了open_files_limit = 65535,重启一看SHOW VARIABLES LIKE 'open_files_limit'还是 1024,原因就是 systemd 的LimitNOFILE把进程的 fd 上限给卡住了,MySQL 在启动时会做一次 min 运算,取各个来源里最小的那个值。这个坑下面还会专门讲怎么排查。

2.3 一个可以直接套用的估算表格

为了让这套算法落地更顺,我整理了一张常用的档位对照表,你可以根据自己的机器规格直接对号入座,然后再做微调。

机器内存缓冲池建议单连接峰值估算推荐 max_connections需要的 nofile
8GB4GB2MB300 ~ 5004096
16GB10GB3MB800 ~ 12008192
32GB20GB3MB1500 ~ 250016384
64GB40GB4MB2800 ~ 400032768
128GB80GB4MB5000 ~ 800065535
256GB160GB4MB8000 ~ 1200065535

这张表的前提是单机 MySQL,业务以 OLTP 为主。如果你的场景是报表分析、大批量导入、或者跑着一堆定时任务,单连接的内存消耗会明显偏高,建议在此基础上打七折。反过来,如果你的业务非常轻,全是主键查询,那可以适当往上浮动。

注意:表格里的"推荐值"是健康运行的上限参考,不是"必须配到这个数"。实际配置应该以Max_used_connections的历史峰值为基准,留出 30% 到 50% 的余量就够了。常年连接数只用到 200 的系统,配上 8000 除了增加内存基线占用之外没有任何收益。

3. 三种改法:临时救火、永久生效、启动参数覆盖

3.1 SET GLOBAL 临时救火:先止血再治伤

线上正在报Too many connections的时候,第一反应应该是先让服务恢复,而不是去翻配置文件。max_connections在 MySQL 5.7 和 8.0 里都是动态变量,支持在线修改,立刻生效,不需要重启。

-- 先看看现在是多少 SHOW VARIABLES LIKE 'max_connections'; -- 临时提到 2000,立刻生效 SET GLOBAL max_connections = 2000; -- 确认生效 SHOW VARIABLES LIKE 'max_connections';

这三行命令在有 SUPER 权限的会话里执行,能在一两秒内把新的连接放进来。但有两个细节必须心里有数:一是这个改动重启就没了,配置文件里没写的话,下次实例重启又回到原值;二是提权限的时候如果实例已经连满了,你可能连一个管理会话都拿不到,这时候要么走本地 socket 连接(很多时候本地 socket 的连接数限制是单独的,或者干脆被预留了),要么就得用mysqladmin的紧急方式,实在不行只能重启实例。

关于"连管理会话都进不去"这件事,有一个常被忽视的保底手段:MySQL 支持extra_port这个额外端口(需要插件或特定构建支持),或者更简单的做法是保留一个常驻的空闲管理连接,在连接池之外单独维持。我自己维护的实例上,通常会保留一个跑sleep的超级权限会话,平时不用,专门用来在事故时救场。

提示:SET GLOBAL改的是全局变量,只对新建立的连接生效,已经存在的连接不受影响。所以你不能靠这个操作去清理现有连接,那得用KILL命令,而且要谨慎——杀错连接可能直接打断一个正在提交的事务。

3.2 配置文件永久生效:写对位置比写对内容更重要

临时止血之后,真正的修复是落到配置文件。MySQL 的配置文件加载顺序在不同发行版和安装方式下差别很大,写错地方是特别常见的翻车点。常见的几个位置是/etc/my.cnf、/etc/mysql/my.cnf、/etc/mysql/mysql.conf.d/mysqld.cnf、$DATADIR/my.cnf,具体哪个是"真正被读取的那个",可以用下面这条命令查:

# 看实例实际加载了哪些配置文件 mysql --help | grep -A 2 "Default options" # 或者直接问实例 mysql -e "SHOW VARIABLES LIKE 'datadir';"

找到正确的文件后,在[mysqld]段落下写配置。一个完整的连接相关配置块大概长这样:

[mysqld] # 核心参数 max_connections = 3000 max_connect_errors = 100000 # 文件描述符,要和 systemd 的 LimitNOFILE 对齐 open_files_limit = 65535 # 连接超时,避免空闲连接长期占坑 wait_timeout = 600 interactive_timeout = 600 # 线程缓存,减少频繁建连销毁的开销 thread_cache_size = 128 # 连接建立阶段的握手超时 connect_timeout = 10

max_connect_errors这个参数单独说一下。它控制的是同一个主机在连续失败多少次之后会被拉黑,默认值是 100,触发之后对方会收到Host 'x.x.x.x' is blocked的错误。在连接池环境下,如果应用重启或者网络抖动导致短时间大量握手失败,很容易把这个计数打满,然后整个 IP 就被拒了。我的习惯是直接调到 100000,同时配合FLUSH HOSTS命令做应急清理。当然这只是缓解手段,真正的解法还是查清楚握手失败的原因。

改完配置文件之后,必须重启或者至少 reload 才能让非动态参数生效。这里有个认知偏差要纠正:open_files_limit在很多版本里不是完全动态的,thread_cache_size是动态的但改配置文件也需要 reload 才会读。重启之前记得先看一眼错误日志有没有配置语法问题,别一重启起不来了。

3.3 启动参数与容器环境:最后一层覆盖关系

除了配置文件,还有一层是启动命令行参数。用mysqld_safe或者直接mysqld启动的时候,命令行里的--max-connections=xxx优先级高于配置文件。这在排查"我明明改了配置文件为什么没生效"的时候特别有用——先看启动命令里有没有覆盖。

# 看进程的实际启动参数 ps -ef | grep mysqld # systemd 管理的实例 systemctl cat mysql systemctl show mysql | grep -i limit

容器化部署的场景下,这层覆盖关系更复杂。Docker 的--ulimit nofile=65535:65535、Compose 里的ulimits配置、Kubernetes 的securityContext或者 Pod 的LimitNOFILE,都会影响最终的 fd 上限。我遇到过一次特别典型的情况:配置文件里open_files_limit = 65535,镜像启动脚本里也设了,但SHOW VARIABLES出来还是 1048576,看着像是生效了,实际上是被容器运行时的默认值给覆盖了,反而比配置值更大——这种"意外生效"虽然结果不错,但属于不可控,该显式声明还是要显式声明。

容器里还有一个坑是内存限制。宿主机 128GB 内存,容器 limit 设了 16GB,你把max_connections按宿主机的规格调到 8000,结果容器一跑起来就 OOMKilled。这种情况要按容器 limit 来算连接预算,不是按宿主机。

4. 只改 max_connections 是不够的:配套参数联动

4.1 文件描述符上限:最容易"改了不生效"的一环

open_files_limit的取值逻辑是这样的:MySQL 启动时会读配置文件里的值,同时也会去问操作系统要,最终取配置值、系统限制、系统硬限制三者中的合理组合。如果配置里写的 65535 超过了 systemd 的LimitNOFILE,那实际生效的就会被压到 systemd 那个值。

排查顺序建议按这个来:

# 第一步:看进程实际的限制 cat /proc/$(pidof mysqld)/limits | grep -i "open files" # 第二步:如果不够,看 systemd 的配置 systemctl show mysql | grep -i nofile # 第三步:改 systemd mkdir -p /etc/systemd/system/mysql.service.d/ cat > /etc/systemd/system/mysql.service.d/override.conf <<'EOF' [Service] LimitNOFILE = 65535 LimitNPROC = 65535 EOF systemctl daemon-reload systemctl restart mysql

对于非 systemd 的老系统,走/etc/security/limits.conf:

mysql soft nofile 65535 mysql hard nofile 65535

改完之后一定要重新登录会话或者重启服务才能生效,ulimit -n验证一下。这一步做完,再回头看SHOW VARIABLES LIKE 'open_files_limit',数字通常就对了。

注意:调 fd 不是越高越好。系统的fs.file-max是全局上限,cat /proc/sys/fs/file-max能看到。如果max_connections给得太大导致 fd 被大量占用,系统里其他进程也可能跟着受影响。所以这两个值要一起看,保持一个合理的比例。

4.2 连接相关超时:把"僵尸连接"清出去

连接数打满,很多时候不是因为并发真的有这么高,而是因为大量空闲连接没被回收。这类连接的典型特征是Threads_connected很高但Threads_running很低,SHOW PROCESSLIST里一堆Sleep状态的会话,并且Time值动辄几千上万秒。

控制空闲连接回收的关键是wait_timeout和interactive_timeout。前者管非交互连接,后者管交互式连接(比如你用命令行客户端连上去)。两者的默认值都是 28800 秒也就是 8 小时,这个值在现代应用架构里明显太长——应用重启、容器漂移、网络断开之后,服务端根本不知道对面已经死了,连接会一直挂着直到超时。

SHOW VARIABLES LIKE '%timeout%'; -- 按业务实际调整,800~1200 秒是比较常见的区间 SET GLOBAL wait_timeout = 900; SET GLOBAL interactive_timeout = 900;

这个值怎么定?我通常看应用侧连接池的idleTimeout配置。原则是数据库的 wait_timeout 要大于应用侧连接池的空闲超时,否则会出现"连接池以为连接还活着,拿起来一用发现服务端已经断了"的情况,报Communications link failure或者The last packet successfully received from the server was xxx milliseconds ago。差个几十秒的缓冲比较合适,比如连接池idleTimeout是 600 秒,数据库wait_timeout就设 900 秒。

还有一个参数max_connect_errors前面提过了,这里补充一个数据清理动作:一旦某个 IP 被 block,FLUSH HOSTS可以立刻解除,不需要重启。

4.3 线程缓存与内存缓冲:连接数上去之后的隐形开销

thread_cache_size这个参数很不起眼,但在连接数规模上去之后影响不小。MySQL 在连接断开之后并不会立刻销毁线程,而是把它放进缓存池,下一个新连接直接复用。缓存太小,高并发场景下会不停地创建和销毁线程,Threads_created飙升,CPU 在上下文切换上白白烧掉。

-- 看线程创建的频率 SHOW STATUS LIKE 'Threads_created'; SHOW STATUS LIKE 'Connections'; -- 如果 Threads_created / Connections > 1%,就该加缓存了

我的配置经验是thread_cache_size取8 + max_connections / 100,比如 3000 连接就配 128 左右,再观察一段时间的Threads_created增长曲线做微调。这个值不要设太大,缓存本身也占资源。

另外,MySQL 8.0 的performance_schema内存占用是跟连接数正相关的。每个连接的 instrument 和 consumer 都要占内存,连接数上到几千之后,performance_schema单独吃掉几个 GB 是常有的事。如果你的实例对内存特别敏感,可以考虑关闭部分 instrument,或者在performance_schema_max_thread_instances上做限制。

最后一个容易被忽略的是连接池中间件。如果你的架构里数据库前面还有 ProxySQL、MySQL Router 或者自研的数据库代理,那实际打到 MySQL 的连接数是代理侧连接池数量,而不是应用侧实例数。这种情况下调 MySQL 的max_connections要跟代理侧的配置对齐,否则应用侧扩缩容对数据库完全没有感知,连接数是个固定值。

5. 实操全过程:从 100 调到 3000 的一次完整记录

5.1 现状摸底:先把基线数据拿到手

假设有一台 32GB 内存、16 核的实例,跑着若干业务服务,当前max_connections = 100,高峰期开始报Too many connections。动手之前,先花十分钟把现状摸清楚。

-- 当前参数 SHOW VARIABLES WHERE Variable_name IN ( 'max_connections', 'open_files_limit', 'thread_cache_size', 'wait_timeout', 'interactive_timeout', 'max_connect_errors', 'version', 'thread_stack' ); -- 历史峰值和最忙时的连接情况 SHOW GLOBAL STATUS WHERE Variable_name IN ( 'Max_used_connections', 'Threads_connected', 'Threads_running', 'Threads_created', 'Connections', 'Aborted_connects', 'Connection_errors_max_connections', 'Uptime' );

Max_used_connections是自实例启动以来的历史峰值,这个数字最有参考价值。如果它是 98,而max_connections是 100,说明确实被打满了;如果它是 60,那说明打满的时刻可能只是瞬时尖刺,要看Connection_errors_max_connections的值来判断。

顺便看一下系统层面:

free -h cat /proc/$(pidof mysqld)/limits | grep -i "open files" nproc ulimit -n

5.2 修改与验证:一步步来,每步都验证

第一步,临时提上去止血,让业务先跑起来:

SET GLOBAL max_connections = 3000;

第二步,把 systemd 的LimitNOFILE调上去,这是真正决定 fd 上限的地方:

mkdir -p /etc/systemd/system/mysql.service.d/ cat > /etc/systemd/system/mysql.service.d/override.conf <<'EOF' [Service] LimitNOFILE = 65535 LimitNPROC = 65535 EOF systemctl daemon-reload

第三步,配置文件里落死参数:

[mysqld] max_connections = 3000 open_files_limit = 65535 max_connect_errors = 100000 thread_cache_size = 128 wait_timeout = 900 interactive_timeout = 900 connect_timeout = 10

第四步,重启并验证:

systemctl restart mysql
SHOW VARIABLES LIKE 'max_connections'; -- 期望 3000 SHOW VARIABLES LIKE 'open_files_limit'; -- 期望 65535 SHOW VARIABLES LIKE 'thread_cache_size'; -- 期望 128
cat /proc/$(pidof mysqld)/limits | grep -i "open files" # 期望输出中包含 65535

第五步,做一次可控的连接压测,验证新上限是真实可用的而不是名义上的。用sysbench或者写个简单的并发脚本都行,重点不是压出多高的 QPS,而是确认能稳定跑在 2000 以上的连接数并且错误率为零:

sysbench oltp_read_write \ --mysql-host=127.0.0.1 \ --mysql-port=3306 \ --mysql-user=test \ --mysql-password=xxx \ --mysql-db=testdb \ --tables=10 \ --table-size=100000 \ --threads=2000 \ --time=120 \ run

压测过程中盯着Threads_connected、Threads_running、Aborted_connects和系统的free、vmstat 1。如果Threads_running一直在个位数而Threads_connected稳定在 2000 以上,说明连接可以被正常维持;如果Aborted_connects开始增长,那多半是 fd 或者back_log的问题。

5.3 压测后的指标复盘:这几个数字要会看

压测结束之后,几个关键指标的解读方式值得单独说清楚。

Max_used_connections / max_connections这个比值,健康区间是小于 80%。如果长期贴着 90% 以上,说明连接池容量规划偏紧,或者存在连接泄漏,得从应用侧解决而不是继续往上加数据库的连接数。

Aborted_connects增长说明握手阶段就失败了,常见原因是认证失败、connect_timeout太短、back_log队列溢出。back_log控制的是 TCP 连接等待队列的长度,高并发建连场景下默认值往往不够,适当调大能明显降低握手失败率。

Connection_errors_max_connections是直接反映"被 max_connections 挡住"的计数器,这个值只要不为零,就说明你的配置还没到位。

Threads_created / Connections的比值反映线程复用效率,理想情况是极低。如果这个比值持续偏高,说明thread_cache_size不够,或者连接的生灭太频繁。

Innodb_buffer_pool_wait_free如果开始增长,说明缓冲池在等空闲页,这通常和内存压力有关,是连接数调高之后内存吃紧的一个早期信号。

6. 常见问题与排查技巧实录

6.1 改了配置文件但值没变:五层覆盖关系挨个排

这个问题出现的频率高到可以单独开一节。MySQL 的参数最终取值会经过这么几层:编译默认值 → 全局配置文件 → 实例专属配置文件 → include 引入的文件 → 命令行参数,后一层覆盖前一层。你改的那一层如果不是最终生效的那一层,改了等于没改。

排查的最快手段是让实例自己告诉你:

SHOW VARIABLES LIKE 'max_connections'; SELECT @@GLOBAL.max_connections;
# 看进程真正读的配置文件路径和启动命令 ps -ef | grep -i mysqld | tr ' ' '\n' | grep -E "defaults|max-connections|cnf" strace -f -e trace=openat -p $(pidof mysqld) 2>&1 | grep -i cnf

还有一个非常隐蔽的坑是配置文件的段落写错。[mysqld]、[mysqld_safe]、[client]这几个段落的参数作用域完全不同,把max_connections写进[client]段里,MySQL 启动时会直接忽略,而且不报错。我第一次遇到这个问题的时候,盯着文件看了半小时才发现是段落错位。

6.2 Too many connections 的真实成因远不止一个

报这个错的时候,第一反应是"连接数不够",但真正常见的原因里,"配置小了"只占一部分。我把遇到过的情况归成几类:

  • 连接泄漏:应用拿了连接没归还,最常见的是异常路径下close()没走到,或者事务没提交导致连接一直挂在Sleep状态。排查方式是看SHOW PROCESSLIST里Sleep状态超过几百秒的连接有多少,以及它们的来源 IP 和用户。
  • 慢查询占坑:某条 SQL 跑了半小时,占着连接不放。这种在SHOW PROCESSLIST里能看到Time很大且Command不是Sleep。
  • 连接池配置过小或过大:过小会限制吞吐,过大会瞬时打满数据库。有一个反直觉的现象是:把应用侧连接池调大之后,数据库反而更容易报Too many connections,因为每个实例的池子都变大了,总和超了。
  • 批量任务集中触发:定时任务在整点集中跑,瞬时连接需求是平时的好几倍。这种情况更合理的做法是错峰或者加任务队列,而不是无限加数据库连接。
  • 中间件的连接放大:代理层为每个客户端连接开一个后端连接,看起来只有几百个客户端,实际打到 MySQL 的是好几千。

我个人的排查顺序是:先看Threads_running,如果它远小于Threads_connected,那就是空闲连接的问题,往泄漏和超时方向查;如果两者接近且都很高,那才是真正的并发压力,可以考虑加连接或者优化 SQL。

6.3 问题速查表

现象可能原因排查动作处理方式
报Too many connections连接数配置偏低Max_used_connections接近上限提高max_connections
同上,但连接数没用满连接泄漏PROCESSLIST看长时间 Sleep修应用侧连接归还逻辑
改了值不生效配置层被覆盖ps -ef看启动参数改对配置文件或调整覆盖层
open_files_limit上不去systemd 限制cat /proc/pid/limits加override.conf
报Host is blockedmax_connect_errors打满SHOW STATUS LIKE 'Aborted_connects'调大阈值 +FLUSH HOSTS
频繁Communications link failurewait_timeout小于连接池空闲时间对比两侧配置数据库超时设得更大
连接数上去了但性能反而下降内存或上下文切换瓶颈vmstat 1、free -h降低连接数,优化 SQL
重启后又回到老值只做了SET GLOBAL检查配置文件写入配置文件

6.4 几条从事故里换来的经验

说完技术细节,分享几条不太适合写进文档但确实有用的经验。

第一,永远别在高峰期改这个参数。SET GLOBAL虽然不需要重启,但连接数突然放开之后,涌入的连接会带来一波新的内存分配和线程创建,短时间的内存抖动足以让一个本来就在临界点的实例撑不住。要改放到低峰期,改完观察半小时再说。

第二,改之前先备份配置文件。听起来很废话,但我见过不止一次因为配置写错一个字母导致实例起不来的情况,有备份的话两分钟能回滚,没备份就得凭记忆一行一行比对。

第三,给max_connections留一条"软着陆"的路。比如你计划调到 3000,可以先调 1500 跑一周,看Max_used_connections的真实峰值是不是真的超过 1500 了。很多时候真实需求远低于你的预期,一次调到位反而浪费资源。

第四,把连接数相关的监控做在前面。我常配的几个告警是:Threads_connected / max_connections > 80%、Connection_errors_max_connections增长超过阈值、Aborted_connects每分钟增长超过 10 次。前两个是容量问题,后一个是握手问题,分开告警能省下大量排查时间。

第五,别迷信大数字。16384 这个数字在网上流传很广,但它对应的是特定规格的机器和特定的业务形态。一台 16GB 的机器照着抄,结果只会是反复 OOM 和重启。参数永远是配套资源的结果,不是可以独立追求的目标。

最后再补充一个容易被忽略的点:max_connections调大之后,SHOW PROCESSLIST的输出会变得非常长,用mysqladmin processlist或者图形工具去看的时候可能直接卡死。这时候要么加上过滤条件,要么改用performance_schema里的threads表去查,效率会高很多,也能避免因为查连接信息本身又占一条连接。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/2 8:33:36

智能体框架 Hermes Agent v0.21:架构、子系统与执行路径全解析

如果你刚接触 Hermes Agent&#xff0c;第一眼看到它的目录结构大概率会有点懵&#xff1a;一堆模块、一堆适配器、一堆配置文件&#xff0c;根本分不清谁是谁。但真正把它跑起来&#xff0c;观察一次完整任务从发起到落地的全过程&#xff0c;你会发现这套系统的架构其实相当收…

作者头像 李华
网站建设 2026/11/2 8:33:36

零基础入门机器视觉:Sherlock软件位置标定实战指南

简介&#xff1a;这份PPT面向机器视觉初学者与自动化检测工程人员&#xff0c;系统讲解Sherlock机器视觉应用软件的使用方法&#xff0c;帮助读者快速建立从界面认知到算法配置的完整知识框架。压缩包内仅含1个PPT文件&#xff0c;大小约3.93MB&#xff0c;以图文页面串联软件定…

作者头像 李华
网站建设 2026/11/2 8:33:08

智能体协作范式:状态机驱动的多Agent系统设计

1. 项目概述&#xff1a;这不是写代码&#xff0c;是在搭建智能体的“操作系统”“Hello-Agents task3——智能体经典范式构建”&#xff0c;光看标题&#xff0c;很多人第一反应是&#xff1a;“又一个AI教学Demo&#xff1f;”但如果你真动手做过三个以上智能体项目&#xff…

作者头像 李华
网站建设 2026/11/2 8:32:35

博士论文初稿交给导师前,我把长段落全部拆短了,硕词AI(官方网站:www.shuociai.com)标了集中区

博士论文初稿交给导师之前那个晚上&#xff0c;我做了一件看似不起眼的事&#xff1a;把全文里那些连续三四百字的长段落&#xff0c;全部拆短了。不是为了好看&#xff0c;而是我发现这些长段落既是阅读的障碍&#xff0c;也是最容易被判定为生成痕迹的地方。 连续长段落的风…

作者头像 李华
网站建设 2026/11/2 8:32:34

Vue.js构建太阳能发电监控系统:架构设计与核心实现

光伏电站的运维现场有个很尴尬的现实&#xff1a;逆变器、汇流箱、电表各说各话&#xff0c;厂家自带的监控平台互不相通&#xff0c;值班人员一天要开三四个后台&#xff0c;数据对不上还得靠手抄。我接触过几个分布式电站的运维团队&#xff0c;他们最朴素的诉求就是"能…

作者头像 李华
网站建设 2026/11/2 8:31:19

免费高并发API平台实战评测与优化指南

1. 宝藏API平台深度评测&#xff1a;免费高并发资源的实战指南最近在技术社区发现一个被疯狂转发的API平台&#xff0c;号称"免费、高并发、专治开发资源匮乏"。作为常年被API调用配额和性能问题折磨的开发者&#xff0c;我第一时间进行了全面测试。这个平台确实解决…

作者头像 李华