新闻详情

新闻详情

首页 / 资讯中心 / 详情

GaussDB轻量化版连接数打满排查实录:从连接池参数到事务超时根治

发布时间:2026/9/29 16:09:18来源:尧图网络
GaussDB轻量化版连接数打满排查实录:从连接池参数到事务超时根治
没经历过的人可能觉得这事挺玄学CPU才20%内存也剩一大半数据库怎么就连不上了我前段时间就在GaussDB轻量化版25.1.30内核版本505.2.1上遇到了这个经典问题——业务使用率不高但数据库连接数直接打满新连接全被拒绝应用那边报错一大片。排查到凌晨才理清头绪。这里把整个分析和解决过程记录下来给遇到同样坑的朋友一个参考。先说结论这类问题十有八九不是数据库真的满了而是连接的去路和来路出了偏差。当数据库资源使用率不高但连接数饱和时本质上是连接生命周期管理失效了——要么连接没被正常释放要么连接池配置和数据库上限不匹配要么有会话卡在事务里长期占着位置。GaussDB轻量化版因为部署环境通常资源有限这个问题更容易被放大。1. 问题现象与第一波排查1.1 故障表现与报错现场我接手的时候应用侧已经炸了。Java微服务的连接池用的是HikariCP报错日志里刷屏的是SQLException: FATAL: sorry, too many clients already Connection is not available, request timed out after 30000ms数据库侧通过gsql登录时直接提示FATAL: remaining connection slots are reserved for non-replication superuser connections登录都成问题因为普通连接名额全部被占用只剩下为超级用户保留的几个槽位。我只好先在CN节点上用管理员账户强登进去先保一条通道再说。通过监控平台看了一下整体负载CPU使用率15%~25%内存使用率不到60%磁盘IO很低慢SQL数量也不多。也就是说数据库本身的硬件负担非常轻但连接数指标一直顶在1000左右不再回落——我用show max_connections;查了下确实是1000。使用率不高却连接打满这就是典型的假性资源耗尽。1.2 第一反应查连接数分布登录进去第一件事就是看当前的连接到底被谁占着。GaussDB兼容PostgreSQL的视图体系直接用pg_stat_activity就够用了SELECT count(*) AS total, state, usename, datname FROM pg_stat_activity GROUP BY state, usename, datname ORDER BY total DESC;结果让我有点意外idle状态的连接占了700多active状态只有不到30个idle in transaction状态的连接有60多个其他是fastpath、prepared等杂七杂八的。也就是说绝大部分连接都是空闲状态这就很说明问题了——应用侧的连接池申请了太多连接但业务一结束并没有真正归还给数据库释放。连接池里的连接在数据库侧看起来就是长时间idle的僵尸连接。这里多说一句连接池里的连接只要不主动close数据库侧就一直维持着会话。HikariCP、Druid这类连接池默认情况下如果最大连接数配置得很大并且空闲回收时间设置得过长那么连接数会持续堆积到池子的上限进而撞上数据库的max_connections。2. 追根溯源为什么使用率不高连接还是会满2.1 连接数的构成逻辑数据库连接数并不是单纯的有多少个客户端连上了它由几部分组成应用连接池创建的物理连接运维工具、监控探针建立的连接GaussDB自身维护的线程池/连接池连接如pooler连接正在执行事务、挂着游标、锁等待的会话。在大多数ERP、OA、报表类系统中后端的业务量不大真正处在active状态的查询很少但连接池为了保证响应速度、避免频繁建连往往会长时间保持一批空闲连接。这批空闲连接在数据库里不会释放依然占用连接名额。在GaussDB轻量化版这种资源受限场景下max_connections设置得本来就不够大很多默认模板里可能只配了500或1000如果应用多个服务节点每个都配了200~300的连接池上限累积起来很容易就把数据库的连接额度耗尽。2.2 连接池参数不合理是最大嫌疑我调出应用侧的配置发现两个服务里HikariCP的配置大概是这样的spring: datasource: hikari: maximum-pool-size: 300 minimum-idle: 50 idle-timeout: 600000 max-lifetime: 1800000 connection-timeout: 30000两个服务节点每个maximum-pool-size都是300理论上最大会占用600个连接。单看好像还不到1000但加上其他监控工具、报表系统、后台任务600个连接再叠加数据库自身的一些连接开销就已经逼近上限了。更关键的是idle-timeout设置为10分钟600000毫秒。也就是说连接空闲超过10分钟后才会被回收。如果业务有间歇性峰值每个峰值周期内都会新建一批连接然后在低峰期慢慢等到超时期间这些连接全都占着连接数不放。这个配置在连接数配额宽松的场景下没问题但在轻量化版本上就非常危险。2.3 应用是否及时归还连接除了连接池参数还需要确认应用代码里有没有正确执行获取连接-关闭连接的逻辑。Spring的Transactional可以管理事务边界但如果在代码里手动获取了Connection却忘了在finally块里close那么这条连接就会被连接池标记为占用虽然业务不忙连接池却始终认为连接正在使用中导致有效连接数持续下降。我实际排查中发现有一个定时任务模块从HikariCP获取连接后在某个异常分支里没有执行close导致每次定时任务执行都会漏掉几条连接。日积月累即使设置了空闲回收被标记为使用中的连接也不会被回收直接持久占坑。2.4 事务中空闲的会话idle in transaction也是连接数消耗大户。比如应用开启了一个事务执行了一条查询然后应用代码中去处理其他业务逻辑迟迟不提交也不回滚。这种会话在数据库侧显示为idle in transaction它们会一直持有连接并且可能持有锁。如果多个这样的会话同时存在不仅占连接数还会让后面的更新操作排队等锁进一步拉高活跃连接。在我这次的故障里就有60多个idle in transaction连接多数是某个报表接口调外部系统超时事务一直开着等外部响应一等就是几分钟。这些事务一旦多起来连接和锁全被占住了。3. 深入定位把罪魁祸首揪出来3.1 查询具体会话信息光看聚合统计还不够得把具体会话列出来。我用下面这条SQL把空闲连接和事务中空闲的连接按持续时间排序SELECT pid, usename, application_name, client_addr, state, query, state_change, xact_start, backend_start FROM pg_stat_activity WHERE state ! active AND pid pg_backend_pid() ORDER BY coalesce(xact_start, state_change) ASC LIMIT 100;结果发现大量连接都是同一个应用服务IP发出的application_name都是同一个微服务名state是idle。这些连接的state_change时间已经很旧了有的甚至保持了几个小时。说白了这些连接就像占着停车位不走的老车资源被白白耗着。我再用下面这个SQL看了一眼每个应用占用的连接总数SELECT client_addr, usename, application_name, count(*) AS conn_cnt FROM pg_stat_activity GROUP BY client_addr, usename, application_name ORDER BY conn_cnt DESC;排名第一的客户端IP来自报表服务占掉了400多个连接。另一个定时任务服务占了150个左右。剩下的分散在监控、运维、以及其他业务模块。3.2 检查是否有长事务和慢SQL接着看active状态里有没有长时间不结束的查询SELECT pid, now() - query_start AS query_runtime, state, wait_event_type, wait_event, query FROM pg_stat_activity WHERE state active AND query NOT ILIKE %pg_stat_activity% ORDER BY query_start ASC LIMIT 50;倒是有几个查询运行了超过10分钟但数量不多不是导致连接满的直接原因。这里要说个经验当连接数打满的时候即使这些慢SQL本身不占太多资源它们也会因为拿不到新连接而无法执行形成应用等待连接-连接被占-应用继续申请新连接的恶性循环最后就是看起来活跃连接不多但连接数完全锁死。3.3 查询连接池状态GaussDB/PG视角GaussDB布丁是基于PostgreSQL内核演进的可以查阅pg_settings里和连接相关的参数SELECT name, setting, unit, context, short_desc FROM pg_settings WHERE name IN (max_connections, superuser_reserved_connections, idle_in_transaction_session_timeout, statement_timeout, tcp_keepalives_idle, tcp_keepalives_interval, tcp_keepalives_count);这能快速了解当前配额和超时设置。如果idle_in_transaction_session_timeout是0说明数据库允许事务无限期地空闲在那里这是很危险的配置。我的环境里这个值果然是0意味着那60多个idle in transaction会话只要应用不提交数据库就一直等下去。4. 处理方案先救急再治本4.1 紧急止血清理空闲会话故障处理的第一原则是先恢复服务。我使用管理员账户手动终止了那些持续很长时间的idle和idle in transaction会话-- 终止某个pid SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state idle AND usename NOT LIKE postgres AND pid pg_backend_pid(); -- 终止所有 idle in transaction 会话业务确认可以杀掉的情况下 SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state idle in transaction AND pid pg_backend_pid();执行后连接数马上从1000掉到了200多应用侧立刻恢复正常。但杀死会话只是临时措施过不了多久又会积攒起来必须接着调整配置和业务逻辑。有一点要特别提醒杀会话前最好确认没有正在执行的关键业务至少要先看xact_start和query字段确认是不是长事务或重要事务。像pg_terminate_backend会直接终止事务并回滚如果这个事务写了一半数据丢失是小事导致主从复制中断就麻烦了。我这次因为是空闲事务且业务量小才敢批量处理。4.2 调整数据库连接配置考虑到轻量化版的资源限制我决定把max_connections从1000调低到500同时设置会话超时参数让数据库主动清理异常空闲连接ALTER SYSTEM SET max_connections 500; ALTER SYSTEM SET superuser_reserved_connections 12; ALTER SYSTEM SET idle_in_transaction_session_timeout 60000; ALTER SYSTEM SET statement_timeout 300000;解释一下这几个参数max_connections调低不是为了让数据库更难连而是倒逼应用连接池收敛避免物理连接无限膨胀。500对于这个中低负载场景完全足够。superuser_reserved_connections保留几个超级用户连接防止真的出现连接满时管理员无法登录救急。idle_in_transaction_session_timeout设置为60秒超过60秒还停在事务中不提交的回滚这个会话。能有效干掉那种事务开着死等外部系统的连接。statement_timeout设置为300秒这是给单个查询设置的超时上限防止某条SQL卡死拖着连接不放。修改后需要重启数据库实例或者根据pg_settings的context来判断是否可以reload生效。max_connections这种参数往往需要重启才能生效我是在低峰期做了重启业务侧连接池会自动重连没有造成中断。注意修改max_connections之前要先看看操作系统的进程/线程数限制以及GaussDB轻量化版的内存配置。每个连接都会占用一定的内存通常几MB到几十MB如果取值过大反而可能导致资源不足取值过小则影响业务。我的场景是资源有限调低反而安全。4.3 优化应用连接池配置应用侧的连接池参数必须和数据库配额联动。我把两个服务的HikariCP配置调整为spring: datasource: hikari: maximum-pool-size: 80 minimum-idle: 10 idle-timeout: 60000 max-lifetime: 300000 connection-timeout: 5000这里的思路是maximum-pool-size从300降到80总体两个服务最多160个连接加上其他杂项不会超过200。minimum-idle降到10避免高峰期过后还保留一堆空闲连接占坑。idle-timeout从10分钟降到60秒快速回收空闲连接。connection-timeout从30秒降到5秒如果获取不到连接就快速失败而不是让请求线程长时间阻塞导致业务线程被拖死。max-lifetime设置5分钟确保连接定期重建避免数据库侧老连接被防火墙/网络设备断开后应用还认为可用。调整之后观察几天数据库最大连接数稳定在200左右再也没出现过打满的情况。4.4 修复应用代码里的连接泄漏只调参数还不够之前定位到定时任务模块存在连接泄漏。这里要强调一个常识使用连接池的话从连接池拿到的连接一定要确保所有分支都释放。哪怕发生异常也要在finally里close或者使用Spring的DataSourceUtils来释放。我看了一眼代码问题大概长这样Connection conn null; try { conn dataSource.getConnection(); // 执行SQL // 中间有段逻辑可能抛异常 } catch (Exception e) { log.error(任务失败, e); // 注意这里没有 close(conn) } finally { // 旧代码里没有finally块 }这个BUG导致一旦任务抛异常Connection就永远不归还给连接池。虽然连接池有默认的泄漏检测但检测周期通常较长且只打日志不会自动回收。修复方式很简单加上finally块释放连接Connection conn null; try { conn dataSource.getConnection(); // 执行SQL } catch (Exception e) { log.error(任务失败, e); throw e; } finally { if (conn ! null) { try { conn.close(); } catch (SQLException ex) { log.warn(close connection failed, ex); } } }也可以用Java 7的try-with-resources效果一样。关键是规范不是花活。5. 预防体系让问题不再复发5.1 建立连接数监控与告警经过这次故障我把连接数监控纳入了日常运维。数据库侧做了一个简单的巡检脚本每5分钟采集一次连接数分布SELECT count(*) AS total_conn, count(*) FILTER (WHERE state active) AS active, count(*) FILTER (WHERE state idle) AS idle, count(*) FILTER (WHERE state idle in transaction) AS idle_in_tx FROM pg_stat_activity;同时按照客户端IP和应用名做分组统计一旦某个应用的连接数超过预设阈值比如max_connections的40%就触发告警。监控这种事一定要有基线。没出过故障的时候很多人觉得没必要但吃过亏之后你会发现连接数打满之前通常是有先兆的比如idle in transaction数量持续上升、单个IP连接数线性增长。如果早看到这个趋势根本不用等到故障才动手。5.2 定期清理异常会话我写了一个定时任务每隔一段时间自动清理符合条件的僵尸会话但保留了一定安全边界-- 清理空闲超过30分钟的会话 SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state idle AND state_change now() - interval 30 minutes AND usename NOT IN (postgres) AND query NOT ILIKE %pg_stat_activity%;提醒一下自动清理要谨慎最好只针对空闲超过一定时间的连接且排除掉管理员连接。如果业务本身有长时间挂起的连接需求需要单独加白名单。清理前先统计数量比如先查出待清理的pid列表人工确认没有正在跑批的作业再执行。5.3 建立连接数使用规范事后我把这次的经验整理成几条内部规范数据库连接数配额严格规划每个应用分配的最大连接数之和不能超过max_connections的70%留出缓冲。所有连接池必须设置idle-timeout、max-lifetime、connection-timeout禁止使用无限等待配置。线上禁止手动获取连接不关闭代码里必须使用try-with-resources或finally释放。涉及外部依赖的查询必须设置事务超时避免事务卡在外部调用上。重大变更连接池扩容、新增服务节点前先评估数据库连接配额是否够用。5.4 处理常见的“连接数反复打满”问题速查表我整理了一个速查表做运维的时候可以直接对照排查现象可能原因排查方法解决方向连接数满但CPU/内存低连接池空闲连接过多查pg_stat_activitystate分布调低 maximum-pool-size缩短 idle-timeout大量idle in transaction应用开启事务后不提交查 xact_start 与 state设置 idle_in_transaction_session_timeout修复代码事务边界某个应用IP连接数异常高连接泄漏或连接池过大按 client_addr 分组统计修复代码泄漏重新规划连接池大小连接数缓慢线性增长连接池回收时间过长看 state_change 时间缩短 idle-timeout增加连接池泄漏检测新连接报 too many clientsmax_connections 不足查看 pg_settings合理调大或优化应用连接数大量 active但执行时间很长慢SQL或死锁查 query/query_start/wait_event优化SQL设置 statement_timeout处理锁等待6. 事后复盘几个容易忽略的细节这里再补充几个实际操作中容易踩的坑。6.1 注意轻量化版本的资源限制GaussDB轻量化版本身就是为了节省资源而存在它的内存、CPU、文件句柄限制通常比企业版要苛刻。连接数看似是数据库层面的限制实际上更底层的是操作系统对进程、线程、句柄的限制。如果并发连接数过大数据库进程可能会因为无法创建新的线程或打开新的socket而报错这种情况下即使你调大了max_connections也未必有用。我当时检查了/etc/security/limits.conf里的nofile限制还有GaussDB进程的线程数上限。如果发现cant create thread或者Too many open files这类日志就要先提升操作系统层面的限制再去调数据库参数。6.2 不要盲目杀会话杀会话是紧急手段但不是常规手段。如果你杀掉了一个正在执行批处理的事务可能会导致数据不一致或者批处理重跑。尤其是分布式GaussDB集群中长事务还关联着全局事务ID贸然终止可能影响其他节点。我的经验先查backend_xid和backend_xmin如果是空的或者时间很短的事务可以杀如果是有写事务且有大量更新最好评估影响再动手。另外杀会话之前最好有个终止计划按批次杀边杀边看连接数下降情况不要一把梭把所有非超级用户的连接全杀了万一误伤了核心业务接口就尴尬了。6.3 连接池参数要动态调整不要一次性到位调整连接池参数后观察期很重要。我调整完初始配置后运行了一天发现maximum-pool-size降到80后部分业务的接口出现了偶发的获取连接等待因为这两个服务虽然有300的上限但同时刻真正需要连接的峰值也就是60~70个但因为是并发高频调用瞬间需要量比较大。我后来又上调到120观察稳定后才定稿。连接池配得太小会变成新的瓶颈配得太大又会重新打满数据库。这个平衡点要通过压测和监控数据来校准不能拍脑袋。6.4 使用率不高也可能是假象这次故障里CPU和内存不高是因为大部分连接都处于idle状态。但如果每个连接都占用了一小块内存GaussDB每个连接大概几MB的缓冲800个空闲连接加起来也能吃掉好几个GB内存在轻量化版上影响还是明显的。所以单纯看使用率不高就以为万事大吉是不行的还得看连接数的绝对值、会话状态、连接建立频率这些指标。7. 最后再分享一个实用小技巧这次之后我给GaussDB轻量化版的运维加了一条黄金10分钟检查流程每天随机挑一个非业务高峰的时段执行下面这条SQL快速判断连接池健康程度SELECT state, count(*) AS cnt, round(100 * count(*) / sum(count(*)) OVER (), 2) AS pct FROM pg_stat_activity GROUP BY state ORDER BY cnt DESC;如果idle占比长期超过80%说明连接池参数学费交得不够等着出问题吧。如果idle in transaction占比持续走高那就立刻查代码里的事务边界别等它变成故障。数据库连接数打满这件事在资源充足的大机上可能几年碰不到一次但在轻量化版本上就是天塌下来的级别。其实只要把连接生命周期管好、把参数调对、把监控补上这个问题完全可以被消灭在萌芽状态。我在这套环境上踩过一遍坑希望你不用再踩一遍。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

DeepSeek V4 百万字通读实测:峰谷定价下,TaoToken 统一 Key 怎么配最省 2026/9/29 17:06:21

DeepSeek V4 百万字通读实测:峰谷定价下,TaoToken 统一 Key 怎么配最省

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
WorkBuddy 实战指南:自定义模型配置、Skill 与本地部署避坑 2026/9/29 17:06:14

WorkBuddy 实战指南:自定义模型配置、Skill 与本地部署避坑

1. 为什么我要认真写这篇 WorkBuddy 实战指南第一次打开 WorkBuddy 的时候,我以为它就是个套壳的对话工具,跟市面上那些"AI 工作台"没什么本质区别。直到我在一个真实项目里,用它把一套原本需要三天才能跑完的数据清洗加报告生成流…

阅读更多 →
hindsight与dify结合:打造AI应用自动复盘与提示词优化工作流 2026/9/29 17:06:14

hindsight与dify结合:打造AI应用自动复盘与提示词优化工作流

聊一个最近在AI应用开发者圈子里讨论热度不低的关键词——hindsight。这个词本身是“事后聪明”的意思,但在AI开发语境里,它指的是一套逆向思考的方法论:与其反复调参、不断手改提示词去“试对”,不如让AI自动复盘失败案例、反向生成更优的提示词。配合dify这类低代码AI应用平台…

阅读更多 →
超前进位加法器原理与Verilog实现:CPU算力背后的关键电路 2026/9/29 17:06:14

超前进位加法器原理与Verilog实现:CPU算力背后的关键电路

聊到CPU算力,大家条件反射会想到主频、核心数、缓存、制程工艺,甚至功耗墙、散热设计。但很少有人会追问一个基本功问题:CPU靠什么硬件单元完成一场加法运算?答案是一块面积小得可以忽略、却决定整颗芯片频率天花板的电路——加法…

阅读更多 →
QuickBlue:10分钟向导式AI微服务底座安装 2026/9/29 17:06:14

QuickBlue:10分钟向导式AI微服务底座安装

1. 项目概述:这不是“一键安装”,而是把AI微服务底座的启动门槛从“工程师”拉回到“会点鼠标的人” “向导式安装——10 分钟从零跑起一套 AI 微服务底座”,这个标题里藏着三个被行业长期忽视却极其关键的痛点: 认知断层、环境…

阅读更多 →
Vue项目部署到阿里云服务器全指南:Nginx配置与HTTPS实战 2026/9/29 17:06:07

Vue项目部署到阿里云服务器全指南:Nginx配置与HTTPS实战

一个Vue项目从本地开发环境跑到线上服务器,中间的弯弯绕绕比大多数人想象的多。我在帮朋友部署一个后台管理项目时,亲眼见过他买了阿里云服务器、装好Nginx、把dist目录传上去,结果打开公网IP只看到一个白屏,接着又是一通盲目操作…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉