建连接贵,所以先建一柜子反复借:checked_out = 借走的书 · capacity = 藏书量 · 使用率 = 借出 ÷ 藏书(80% 黄 / 95% 红)。覆盖 内存盘 m115 / m116 / m117 / m113 · biz 盘 b5 / b6 · worker 池 m129 / m130。
去图书馆借书不用自己买书:书(数据库连接)早就建好一柜子,你来借(checked_out 加一),看完还(checked_out 减一),书反复用。为什么这么设计?因为建一条连接很贵——TCP 握手、鉴权几十毫秒——如果每个请求都现建一条、用完就扔,系统会把自己耗死在「买书」上。
台账上最重要的数就一个:使用率 = 借出 ÷ 藏书。80% 是黄色警戒(该留意了),95% 红色(临界),100% = 全借光,后来的请求只能站在柜台前排队等还书——哪怕它只想查一个电话号码。池打满是「接口变慢→雪崩」链条的中继站,所以它同时是铁三角课里说的那个 80% 刹车点。
你是图书馆馆长。书很贵:每进一本新书要走采购、编目、上架一整套流程(对应 TCP 握手、鉴权),如果每个读者来了都现买一本、看完就扔,图书馆早就破产了。所以你一次进够一柜子书,读者来了借,看完还,书被反复借用——这就是连接池。四两的 backend 给 MySQL 办了三张借书证,按用途分了三个柜:sync 30(同步查询)/ async 80(异步)/ checkpointer 30(检查点)。
馆里最要命的时刻是「全借光」。第 81 个读者来了,async 柜 80 本书全在别人手里——他只能等,等到有人还书为止。他可能只是想查个电话号码(一条 1 毫秒的简单查询),也得陪着整个馆等。更糟的是:等的人越多,每个人借书前干等的时间越长(延迟涨),赖在馆里不走的人就越多(并发涨),书还得更慢——这就是铁三角课里那条雪崩链,池子正是它的中继站。
四两的馆还不止一座,各有各的脾气:biz 的柜子平时 10 个位子、高峰临时加座到 30(10 + overflow 20 = 30,overflow 就是折叠椅);Redis 的 shared 柜上限 50,还和 Agent 服务共用——邻居借多了你也受影响;review 服务有个独立小柜 5+5,慢任务一来特别容易被借空。记住:池是 per-worker 的——「sync 30」是每个 worker 各 30 本,4 个 worker 整机是 120 本。
| 类比里的东西 | 系统里对应的东西 |
|---|---|
| 藏书量(柜子容量) | capacity:池上限(backend sync 30 / async 80 / checkpointer 30;biz 10+overflow 20=30;Redis shared 50) |
| 借走的书 | checked_out / in_use:当前借出的连接数(Gauge,直接读) |
| 借书证(按用途分柜) | pool 标签:sync / async / checkpointer / shared / broadcaster_pubsub(广播专柜) |
| 临时加座(折叠椅) | overflow:biz 池平时 10 条,高峰临时开到 30(10 + 20 = 30) |
| 全借光、排队等还书 | 使用率 100% = 池打满:请求排队,延迟涨 → 并发涨 → 排队更狠(雪崩中继站) |
| 图书馆闭馆检修 | DB/Redis 不可用:一本书都借不到,全线报错(与锁冲突、依赖故障面板互证) |
checked_out,Redis 池叫 in_use——名字不同,都是「此刻借出去几条」。它是 Gauge,直接读数。
# m116 口径:各 worker 借出数 + 容量参考线 siliang_db_pool_connections{state="checked_out",job="siliang_backend_worker"}
# 各池的天花板:sync 30 / async 80 / checkpointer 30 max by (pool) (siliang_db_pool_capacity{job="siliang_backend_worker"})
on(instance, pool) 对齐,避免毛刺和错配造成假告警。
# m115 口径:80% 黄 / 95% 红,贴 100% = 池打满 max_over_time(siliang_db_pool_connections{state="checked_out"}[1m]) / on(instance, pool) max_over_time(siliang_db_pool_capacity[1m])
# 按池拆开看谁紧张 max_over_time(siliang_db_pool_connections{state="checked_out"}[1m]) by (instance, pool) # 上限:sync 30 / async 80 / checkpointer 30(per-worker)
# b6 口径:biz 借出数 + 上限 30 参考线(10 + overflow 20) siliang_biz_db_pool_connections{state="checked_out",job="siliang-biz"}
# m117 口径:shared 与 broadcaster_pubsub 各一张借书证 max_over_time(siliang_redis_pool_connections{state="in_use"}[1m]) / on(instance, pool) max_over_time(siliang_redis_pool_capacity[1m])
# 广播池紧张时,去 WS 错误面板看 redis_pubsub 是否大于 0 siliang_redis_pool_connections{state="in_use",pool="broadcaster_pubsub"}
# m130 口径:worker 池绝对值 + 上限参考线 siliang_worker_db_pool_connections{state="checked_out",job="siliang-review"}
# 余量 = 天花板 − 借出(绝对值卡的用法) max by (pool) (siliang_db_pool_capacity) - siliang_db_pool_connections{state="checked_out"}
# m113 口径:1 = 总机存活;有流量时掉 0 即异常 max by (provider) (max_over_time(siliang_shared_http_client_alive[1m]))
① 数据库连接池使用率(m115)——借出 ÷ 藏书,按 worker × 池拆;80% 黄 / 95% 红,贴 100% = 池打满请求排队:🔌 数据库连接池使用率 ↗
② 数据库连接池借出数与容量上限(m116)——绝对值版:实线借出数 + sync 30 / async 80 / checkpointer 30 参考线,看离天花板多远:🔌 数据库连接池借出数与容量上限 ↗
③ Redis 连接池使用率(m117)——shared(业务共用,上限 50 与 Agent 服务共用)+ broadcaster_pubsub(广播专柜)两张借书证:🔌 Redis 连接池使用率 ↗
④ biz 数据库连接池(b5 / b6)——biz 盘的同款镜像:使用率卡 + 绝对值卡,天花板 10 + overflow 20 = 30,按机器 repeat 成对出现:🟢 biz 数据库连接池使用率 ↗ · 🟢 biz 借出数与上限 ↗
⑤ 共享 HTTP client 存活(m113)——出站「电话总机」还在不在:1=存活;有聊天流量时掉 0 = 连接复用回退,FD 与内存跟着抖:🔌 共享 HTTP client 存活 ↗
接口成片超时,m115 一片贴 100%。第一步按池拆看是哪个柜子打满(async 打满多半是慢查询占着不还),第二步对齐铁三角:并发与延迟同时刻异动,坐实「排队」。
# 第一步:哪个 worker × 哪个池在贴 100%? max_over_time(siliang_db_pool_connections{state="checked_out"}[1m]) / on(instance, pool) max_over_time(siliang_db_pool_capacity[1m]) # 第二步:绝对值——离天花板还差几条(m116 口径) siliang_db_pool_connections{state="checked_out"} by (instance, pool) # 第三步:互证雪崩链——同一时刻 P99 是否突起 topk(5, histogram_quantile(0.99, sum(rate(siliang_http_request_duration_seconds_bucket[5m])) by (le, handler))) # 判读:池先满 → 延迟后涨 = 池是根因;反过来 = 延迟把池顶满
判读收尾:找到贴 100% 的那条线对应的 instance + pool,直奔该时段慢查询日志。
两个严重度的同一规则:80% 是刹车点(提醒去查),95% 是临界(准备限流/杀慢查询)。都要求持续 5 分钟,避免抓取毛刺造成狼来了。
groups: - name: siliang-pool rules: - alert: DbPoolUsageWarning expr: | max_over_time(siliang_db_pool_connections{state="checked_out"}[1m]) / on(instance, pool) max_over_time(siliang_db_pool_capacity[1m]) > 0.8 for: 5m labels: severity: warning annotations: summary: "{{ $labels.instance }} 的 {{ $labels.pool }} 池使用率超 80%(刹车点)" - alert: DbPoolUsageCritical expr: | max_over_time(siliang_db_pool_connections{state="checked_out"}[1m]) / on(instance, pool) max_over_time(siliang_db_pool_capacity[1m]) > 0.95 for: 5m labels: severity: critical
判读收尾:阈值与 for 时长以各盘「先读我」口径卡为准,这里是通用模板。
容量巡检不用看百分比,直接算「还剩几本可借」。余量长期小于池的 10%,就该在下次变更里调池或优化慢查询。
# 每个 worker 每个池的剩余可借数 max by (instance, pool) (siliang_db_pool_capacity) - max_over_time(siliang_db_pool_connections{state="checked_out"}[1m]) # biz 同款:天花板是 30(10 + overflow 20),别按 10 算 max (siliang_biz_db_pool_capacity) - max_over_time(siliang_biz_db_pool_connections{state="checked_out"}[1m]) # 预期:平峰余量大于 50% 高峰余量大于 10%,否则列入调参待办
用户反馈「我画了他看不到」,但接口都不慢——这是 broadcaster_pubsub 专柜的事,与业务 DB 池无关。联合 WS 错误面板互证。
# 广播专柜使用率(Redis 池的 pubsub 分柜) max_over_time(siliang_redis_pool_connections{state="in_use",pool="broadcaster_pubsub"}[1m]) / on(instance, pool) max_over_time(siliang_redis_pool_capacity[1m]) # 互证:WS 错误里 redis_pubsub 大于 0 = 跨实例广播失明(c22 口径) sum(rate(siliang_ws_errors_total[5m])) by (error_type) # 判读:广播池贴 100% + redis_pubsub 报错 = 消息发不出去,查 Redis 状态
m113 平时是一条 1 的直线。有聊天流量时某 provider 掉到 0,说明共享 client 没了——系统退回「每次现买书」模式,FD 和内存开始抖动。
# 各 provider 的总机存活(1 = 在,0 = 没了) max by (provider) (max_over_time(siliang_shared_http_client_alive[1m])) # 互证:出站线路占用是否出现锯齿(复用健康时应平稳,m119 口径) siliang_http_client_connections{state="active"} by (instance, client) # 判读:存活掉 0 + 线路数剧烈抖动 = 连接复用回退,按回退事故处理
# 错: max_over_time(checked_out[1m]) / max_over_time(capacity[1m]) # → 错配 # 对: ... / on(instance, pool) max_over_time(siliang_db_pool_capacity[1m]) # → 对齐
# 错: backend sync 池整机 = 30 条 # → 少算 4 倍 # 对: 30 条/worker × 4 worker = 整机 120 条
# 错: 借出 12 ÷ 10 = 120%? # → 分母错了 # 对: 借出 12 ÷ 30 = 40%(10 + overflow 20 = 30)
# 错: shared 池涨 = 我们的问题 # → 可能是 Agent 服务在用 # 对: 按 pool 拆开看:shared 涨查双方,broadcaster_pubsub 涨查广播
# 错: 池满 → 改大池配置 → 两周后再次打满 # 对: 池满 → 找打满时段的慢查询 → 修索引/缩短事务 → 使用率自然回落
# 错: Redis 池 100% → 全站告警乱枪打鸟 # 对: broadcaster_pubsub 满 = 协作失明;shared 满 = 业务抖 → 两套动作
# 错: rate(siliang_db_pool_connections{state="checked_out"}[5m]) # → 无意义 # 对: max_over_time(siliang_db_pool_connections{state="checked_out"}[1m])
参考答案:建一条连接要 TCP 握手加鉴权,几十毫秒起步——比一条简单查询本身还贵。用完就扔意味着每个请求都先交一笔「买书钱」,高峰期大家都在买书而不是看书,系统把自己耗死在握手和鉴权上。池化就是先建一柜子反复借还,让贵的资源被复用。
参考答案:体感是「成片变慢和超时」——新请求借不到连接,在池前排队等还书。先查三件事:哪个 worker 的哪个池打满(m115 按池拆);同时刻 P99 是否突起(坐实排队,c19);打满是不是从某条慢查询开始(占着书不还的是它)。修慢查询是根治,扩池只是推迟。
参考答案:30。组成是「基础 10 + overflow 临时加座 20」——平时最多借 10 条,高峰靠 overflow 临时开到 30。所以看 b5/b6 两张卡时参考线是 30,别拿 10 当分母算出「120% 使用率」的假警报。
参考答案:先记住它不是你独享的——shared 池上限 50,与 Agent 服务共用。所以可能是我方业务涨、可能是 Agent 服务挤占、也可能是 Redis 本身变慢导致连接归还变慢(借书的人不是不还,是还书窗口排队)。按 pool 拆开(区分 shared 和 broadcaster_pubsub)、按 instance 拆开(区分哪台机器),再对时间戳找同时刻异动的邻居。