他的回复:
先说一个前提,再给排查顺序。这个帖子问的是 RDS for MySQL,有一条路在 RDS 上走不通:RDS 不授予 SUPER 权限,不能用 SET GLOBAL 去改 max_connections 这类系统变量,参数要走控制台的参数组改,而且改完是否立即生效、要不要重启,以控制台上的提示为准。另外 RDS 的 max_connections 默认值是按实例内存算出来的,不是固定值,升规格后上限会跟着变,盲目调大也可能把内存问题引出来。所以下面的顺序里,调参数是最后一步,而且是在控制台做的。0-2 分钟:先保住进库通道,别再重试业务已经报 Too many connections 时,不要用新连接反复重试,那会把最后的名额也占掉。用已经建立的连接进,或者走 RDS 控制台的会话管理(DAS 走独立通道,不占业务连接名额)。MySQL 会给有 SUPER/CONNECTION_ADMIN 权限的账号预留一个连接,但 RDS 上你拿不到这个通道,所以 DAS 会话管理就是你的救命通道。一进库先把现场固定下来:把 information_schema.processlist 全量导出来存一份(SELECT ... INTO OUTFILE 或者直接复制到文本里)。杀完就查不到了,复盘全靠这份快照。2-5 分钟:四个数定性,别只看总数SHOW GLOBAL STATUS LIKE 'Threads_connected';SHOW GLOBAL STATUS LIKE 'Threads_running';SHOW VARIABLES LIKE 'max_connections';SHOW GLOBAL STATUS LIKE 'Max_used_connections';SHOW GLOBAL STATUS LIKE 'Connection_errors_max_connections';判读方法:Threads_connected 接近上限、Threads_running 很小,说明连接都被占着但多数空闲,方向是连接泄漏或连接池配得过大;Threads_running 也高,说明连接都在干活,方向是慢 SQL 或锁等待堆积;Connection_errors_max_connections 大于 0,说明确实已经有人被拒绝过;Max_used_connections 接近上限,说明已经没有余量了。再按账号、来源 IP、状态分组,定位大头:SELECT USER, SUBSTRING_INDEX(HOST, ':', 1) AS client_ip, COMMAND, COUNT(*) AS cnt, MAX(TIME) AS max_time_s FROM information_schema.processlist GROUP BY USER, client_ip, COMMAND ORDER BY cnt DESC LIMIT 20;同时看一眼长事务:SELECT trx_mysql_thread_id, trx_started, TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS age_s FROM information_schema.innodb_trx ORDER BY trx_started LIMIT 10;补充一个容易漏的区分:如果是短时间内同一来源反复新建连接,还要看 Aborted_connects 计数和错误日志,区分是业务重试风暴,还是外部在扫端口、暴力破解。前者要从应用侧限流,后者要封 IP、改端口、加白名单,处理方法完全不同。5-10 分钟:应用侧四项必查第一,算连接池总账。所有连这个库的服务,实例数乘以每个实例的连接池上限,再加上定时任务、监控采集、运维工具占的连接,总和要留 20% 到 30% 的余量。常见错误是一个服务 20 个 Pod,每池 50 个连接,合计 1000,而实例上限只有 800。扩容应用副本之前,先算这笔账。第二,空闲回收两端对齐。连接池的 maxLifetime 和 idleTimeout 要小于数据库的 wait_timeout,否则池里会留着已被服务端断开的“死连接”,表现为偶发的 Communications link failure,又会触发重连风暴。第三,泄漏检测。HikariCP 开 leakDetectionThreshold(比如 60000 毫秒),Druid 开 removeAbandoned,能直接在日志里指出是哪段代码借了连接没还。第四,长事务。事务里套 RPC、循环里逐条写、异常分支漏了 commit 或 rollback,都会让连接一直挂着。临时止血:分批杀,别重启只杀长时间 Sleep、来源明确为非核心或已下线服务的连接,分批、逐个 KILL,每次几十条,一边杀一边看业务影响。不要批量杀正在执行的业务连接,更不要重启实例:重启清掉现场,如果是泄漏,几小时后必然复发,等于白丢证据。如果扛不住,从入口限流,或者临时摘掉有问题的服务节点,让核心业务先活着。调大 max_connections 只是最后一步:RDS 上走参数组改,改之前先算内存,每个连接除了线程栈,还会按需分配排序、连接等缓冲区,连接数从几百调到几千,极端并发下内存占用可能成倍上涨。长期治理不同服务用不同数据库账号,必要时用 MAX_USER_CONNECTIONS 限制次要服务,某个报表任务出问题时不会把核心交易的连接名额挤光。只读流量分到只读实例,主库连接压力直接减半。条件允许时用 RDS 数据库代理做连接复用,把应用侧上万条连接收敛成后端几十条。云监控里给连接数使用率配两级告警,70% 到 80% 告警、90% 升级,连续多个周期触发再通知;把 Max_used_connections 和 Connection_errors_max_connections 纳入日常巡检,前者接近上限说明已经没有余量,后者大于 0 说明确实有人被拒过。最后把“连接池总账”写进变更检查项,下次应用扩容前先自查。