news 2026/9/30 2:18:17

【项目记录】数据库集群巡检推送

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【项目记录】数据库集群巡检推送

数据库集群自动化巡检与推送报告系统 —— 项目记录

脱敏声明:本文为技术复盘,文中所有 IP、主机名、域名、账号、邮箱、数据源 UID、大盘 UID、
集群业务名称、部署路径均已替换为占位符(如db-mysql-01、10.20.x.y、grafana.example.com、
业务集群A)。PromQL、指标名、判据、代码结构均保持原样,可直接参考。
不涉及任何真实个人或组织信息。


目录

  1. 为什么做这件事
  2. 两条并行的战线
  3. 巡检覆盖范围
  4. 整体设计
  5. 指标口径定义
  6. 核心代码
  7. 效果展示
  8. 踩过的坑与解决方式
  9. 工程经验总结
  10. 项目产出清单

1. 为什么做这件事

1.1 从「人去查」到「系统告诉人」

数据库集群的容量与健康看板早就有了 —— Prometheus + Grafana,指标齐全。

但现实是:没人会每天早上主动把 N 个大盘翻一遍。

  • 大盘有几十个面板,真正要看的只有 4 个指标(CPU、内存、磁盘 IO、磁盘使用率)
  • 4 个指标分散在不同的大盘上,还要手动切主机变量
  • 「配置了告警」不等于「看懂了告警」—— 阈值告警只告诉你"某个指标越线了",
    而值班的人想知道的是"现在整个集群什么状况、哪几台有问题、问题有多严重"
  • 周期性容量趋势(哪个盘在涨、涨多快)没人做

所以我们做了一个推送到邮箱 + 企微群的巡检报告:不等人来看,每天定时把结论送到眼前。

1.2 原有的巡检脚本有哪些问题

在改造之前,公司内部已经有一套脚本在跑,但暴露出几类典型问题:

问题类别具体表现
口径不统一5 个数据库类型 5 套采集脚本,同一个"内存使用率"有 2 种算法,"磁盘 IO"有 3 种聚合方式,彼此差 1~2 个百分点
指标被平均掩盖磁盘 IO 把主机上所有盘平均,一块盘跑到 99% 而另一块空闲 → 报出来只有 49%,直接漏报
采集漏项MongoDB 脚本"只要有非 sda 的盘就无条件忽略 sda",某些主机真正忙的恰恰是 sda,一直报 0.05%
物理不可能的值irate对外推/多路径设备算出 105.45% 这种 >100% 的磁盘使用率,直接进邮件,没人发现
峰值没有上下文只报时段峰值,不报持续多久。"尖峰 2 分钟"和"持续 203 分钟"在报表里长得一模一样
可解释性差表格列名写「磁盘IO使用率峰值」,但实际是"最忙那一分钟的盘间平均",读邮件的人理解不了
运维性缺失推送脚本退出码永远是 0、没有数据新鲜度校验、SMTP 无超时(卡住就一直挂)、日志无限增长

1.3 目标

  1. 把「数据口径」对齐,并在报表里写清楚每一列是怎么算的
  2. 把「被平均掩盖的问题」暴露出来
  3. 让报表能直接用 —— 值班的人看一眼就知道该处理哪几台
  4. 采集与推送解耦、可测试、可回滚
  5. 不改动正在运行的老脚本,新老并行、可随时退回

2. 两条并行的战线

项目实际覆盖了两套环境,需求差异不小:

线下(内网离线环境)线上(生产环境)
主机数60 台40 台
数据库MySQL / StarRocks / Greenplum / MongoDB / Oracle / RedisMySQL / StarRocks / Greenplum / MongoDB / Oracle
数据源自建 Grafana(三套 Prometheus 联邦)生产 Grafana(默认数据源 + 容器指标源)
采集方式Grafana 为主,SSH + sar/iostat 兜底(部分主机没有监控探针)纯 Grafana
推送邮件 + 企业微信群邮件 + 企业微信群
调度cron 每天 2 次cron 每天 2 次

两条线的指标口径、判定逻辑、报告结构、推送模块是共用一套设计的,
只有"取数"这一层不同 —— 这也是后来能把线上 6 个脚本合并成 1 个的基础。


3. 巡检覆盖范围

3.1 主机与集群

数据库类型集群数主机数备注
MySQL6 个业务集群18 台主从架构,含域名主备切换检测
StarRocks16 台数仓
Greenplum16 台数仓
MongoDB26 台副本集
Oracle24 台含表空间、备库延迟采集
Redis—4 台仅线下

3.2 采集指标

指标含义单位
主机可达性 / 数据库连通性有没有数据 / 端口通不通可达 / 不可达 / 未探测
CPU 使用率峰值主机整体 CPU(或容器 CPU 配额使用率,视集群而定)%
内存使用率峰值按MemAvailable计算,不含可回收缓存%
磁盘 IO 使用率峰值每块盘"忙的时间占比",取窗口内峰值后按口径聚合%
磁盘使用率峰值该主机最满的挂载点%
综合状态上述指标的最差状态严重 / 告警 / 正常

3.3 时间窗口

采用连续窗口模式:上次运行时刻 → 本次运行时刻。

老版本用固定班次(上午 01:30–09:30 / 下午 09:30–17:30),
结果17:30 到次日 01:30 这 8 小时是盲区—— 夜间出问题第二天早上才知道。
改成连续窗口后盲区消除,代价是要维护一个"上次成功运行时刻"的状态文件,
并处理"状态文件丢失 / 长时间没跑"的兜底(限制最长回溯 24 小时)。


4. 整体设计

4.1 数据流

┌─────────────────────────────────┐ │ Prometheus / Grafana 数据源 │ │ (默认源 / 容器指标源 / 多联邦) │ └───────────────┬─────────────────┘ │ Datasource Proxy API │ GET /api/datasources/proxy/uid/<uid>/api/v1/query_range ▼ ┌────────────────────────────────────────────────────────────┐ │ 采集层(Collector) │ │ · 按集群生成 PromQL(instance/ip 正则、设备正则) │ │ · 批量查询 + 按主机聚合 │ │ · 拿不到就降级到 SSH(sar / iostat / free / top) │ └───────────────────────────┬────────────────────────────────┘ │ 结构化数据 ▼ ┌────────────────────────────────────────────────────────────┐ │ 判定层(Judge) │ │ · 峰值 vs 阈值(警告 / 严重) │ │ · 缺数据 → 「无数据」而非「严重」 │ │ · 综合状态 = 各项最差 │ └───────────────────────────┬────────────────────────────────┘ │ ┌──────────────┴──────────────┐ ▼ ▼ ┌──────────────────────┐ ┌──────────────────────┐ │ 渲染层(Render) │ │ 推送层(Push) │ │ · HTML 邮件 │ │ · SMTP_SSL 发信 │ │ · 企微 markdown │ │ · 群机器人 webhook │ └──────────────────────┘ └──────────────────────┘

4.2 采集层:Prometheus 优先,SSH 兜底

不是所有主机都装了 node_exporter。所以采集层做了优先级降级:

Grafana 有数据 → 用 Grafana(快、准、带标签) Grafana 没数据 → SSH 上去跑 sar/iostat/free/top(慢,但不会漏) 两边都没有 → 标记「未探测」,不算「不可达」,避免误报

SSH 兜底的关键工程点是一次连接拿完所有数据:把远程要执行的命令拼成一个脚本,
用@@@分隔各段输出,一次 SSH 会话全部取回。60 台主机 10 并发,整体几十秒。

4.3 判定层:缺数据不等于故障

老脚本的judge_status(None)直接返回「严重」,导致监控探针没装的主机天天报严重。
改成三态:

有值且越线 → 严重 / 告警 有值未越线 → 正常 压根没取到值 → 无数据(独立状态,不参与"最差"计算,也不会让整机变红)

同时在报告顶部显示数据质量:完整 N 台 / 部分缺失 N 台 / 无数据 N 台(缺失 x%)。
这样"报表说没问题"和"报表没看到"能被区分开。


5. 指标口径定义

这一节是整个项目最花时间的部分。同一句话「CPU 使用率」在不同集群里可能是两个完全不同的东西。

5.1 CPU

场景口径含义
容器化 MySQL容器实际用量 / 容器 CPU limit × 100容器 CPU 配额使用率—— 反映业务 pod 用了多少配额
物理机 / 虚拟机100 × (1 − avg(irate(node_cpu_seconds_total{mode="idle"}[5m])))主机 CPU 使用率—— 反映整机所有核的平均繁忙度

⚠️ 这两者量级完全不同(一个是"用了配额的百分之几",一个是"整机有多忙"),
放在同一张表里必须把列名区分开,否则读表的人一定会误判。

5.2 内存

内存使用率 = (1 − MemAvailable / MemTotal) × 100

为什么不用MemTotal − MemFree − Buffers − Cached?

MemAvailable是内核给出的"在不触发 swap 的前提下还能被新应用用掉多少内存"的估算值,
它已经把可回收的 page cache / slab算作可用。而Free + Buffers + Cached是手工拼的,
口径随内核版本和具体场景漂移,通常明显高估内存压力。

实测同一台机器两种算法差30~45 个百分点(一个报 72%,一个报 42%)。
统一成MemAvailable后,内存告警的准确性提升非常明显。

5.3 磁盘 IO 使用率

irate(node_disk_io_time_seconds_total{..., device=~"sd[a-z]"}[5m]) * 100

node_disk_io_time_seconds_total是"这块盘累计有多少秒在处理 IO",
它的一阶导数就是该盘每秒里有多大比例的时间在忙。×100 就是百分比。
100% = 这块盘一直在处理 IO。

要注意它的三个特性:

  1. 反映"忙不忙",不反映吞吐和延迟。一块慢机械盘在很低 IOPS 下也能 100% 忙;
    一块 NVMe 100% 忙时吞吐可能很高。所以它适合发现"盘被打满了",
    不适合判断"性能好不好"。
  2. 必须逐盘算,不能先聚合(见 8.2)。
  3. irate需要区间内至少 2 个采样点,采集间隔必须小于区间长度(见 8.1)。

5.4 磁盘使用率

max by(instance) ((1 - ( node_filesystem_avail_bytes{fstype=~"xfs|ext4"} / node_filesystem_size_bytes{fstype=~"xfs|ext4"} )) * 100)

取该主机最满的挂载点。fstype过滤掉tmpfs/devtmpfs这类假文件系统。
某些集群额外加了挂载点白名单(mountpoint=~"/log|/|/data*"),避免/etc/hosts、
/usr/share/zoneinfo这类固定大小、天然 100% 满的只读挂载点污染结果 ——
这是踩过坑才知道的(见 8.10)。


6. 核心代码

6.1 查询 Grafana 数据源

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

Opik Prompt Playground:不写代码,也能像做实验一样调提示词

如果你正在开发基于大语言模型的应用&#xff0c;大概率会遇到这样的场景&#xff1a;为了让模型输出更符合预期&#xff0c;你反复修改提示词&#xff0c;然后写一段脚本调用 API&#xff0c;把结果打印出来&#xff0c;再手动对比。改了几版之后&#xff0c;你已经记不清哪一…

作者头像 李华
网站建设 2026/9/30 2:13:37

BSDiff 二进制差分与增量更新:补丁生成、还原及 OTA 工程实践

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

作者头像 李华