ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL数据库监测指标体系搭建:采集、阈值与文档化实践

MySQL数据库监测指标体系搭建:采集、阈值与文档化实践 简介数据库监控是保障线上服务稳定性的基础能力但很多团队的监控体系只停留在截图和临时查询。真正有效的监控需要一套可采集、可对比、可告警的数字指标体系并且每个指标的定义、阈值和负责人要长期有效。以MySQL为例监控指标通常分为吞吐、连接、缓存与复制三个维度QPS/线程数反映承载压力连接使用率是故障第一现场InnoDB buffer pool命中率和复制延迟则需要结合磁盘状态综合判断。通过mysqld_exporter将状态变量转为Prometheus时间序列搭配最小权限账号和textfile collector补充自定义指标再基于7天基线数据用quantile_over_time计算p95阈值才能避免告警轰炸。最终把指标名、采集来源、单位、阈值和副作用沉淀为可校验的文档并用脚本同步校验Prometheus中的指标是否存在确保监控体系可持续演进。围绕指标设计、采集落地和配置校验提供一套可直接抄用的命令和配置帮助团队快速搭建可靠的MySQL数据库监测体系。1. 数据库监测指标不是一张截图把数据库监测指标写成一份文档听上去是运维里最简单的事实际上大多数团队交出来的只有监控截图和一句看 Grafana 吧。等真正出故障QPS、连接数、复制延迟散落在不同系统没人说得清哪条指标该告警、阈值是谁定的。数据库监测指标真正要做的事是把数据库还健康吗翻译成一组可采集、可对比、可告警的数字并且让这些数字的定义、口径和负责人长期有效。下面以 MySQL 为例从指标分类、采集落地、阈值设计到文档化校验把整条链路拆开讲命令和配置都可以直接抄。2. 数据库监测指标的三个维度吞吐、连接、缓存与复制2.1 吞吐类指标看趋势别看绝对值QPS 和 TPS 是业务方问得最多的两个数。这两个数本身不构成告警依据真正有用的是变化趋势。PromQL 里一般这样算rate(mysql_global_status_queries[1m])mysql_global_status_queries 是累计值rate 把它变成每秒速率。这个指标适合看曲线有没有明显抬升或下跌——QPS 突然减半比突然翻倍更值得警觉因为常见原因是连接被堵住请求根本进不来。比 QPS 更值得盯的是 threads_running即当前正在执行的线程数。它反映数据库此刻真实的工作压力。CPU 核数为 N 的实例threads_running 持续超过 2N基本可以判断 SQL 已经进入执行层排队。开发同学常问的数据库是不是慢了用这个指标回答最直接。2.2 连接类指标故障第一现场数据库故障最先体现出来的往往不是 CPU而是连接异常。threads_connected 接近 max_connections 时新请求直接报 Too many connections应用侧表现为连接池报错。这里有个经典坑MySQL 的 max_connections 默认只有 151而应用连接池可能按 200 甚至更高去配两边从配置上就没对齐过。连接指标的正确打开方式是看比值mysql_global_status_threads_connected / on (instance) mysql_global_variables_max_connections除了当前连接数还要看 Connection_errors_max_connections 和 Aborted_connects 这两个累计值。前者只要大于 0说明已经发生过连接拒绝后者持续增长通常是密码错误、连接被防火墙掐断或者客户端异常断开这类问题看平均值没用必须看斜率。2.3 缓存、存储与复制延迟要从根上拆InnoDB buffer pool 命中率是 MySQL 最老的监控指标之一但命中率 99% 只说明热数据在内存不能说明磁盘没有压力。要把 innodb_buffer_pool_reads从磁盘读页的累计值单独拉出来配合 node_exporter 的磁盘 await 一起看。命中率高同时 await 高通常是刷新脏页或双写造成的命中率掉到 95% 以下才需要怀疑 buffer pool 容量本身。复制延迟用 seconds_behind_master 就能看但这个指标有个著名的坑SQL 线程空闲时它不更新主从之间网络断了反而可能显示 0。看从库健康度不能只看这一个数还要看 Slave_IO_Running 和 Slave_SQL_Running 两个线程状态以及主从的 GTID 集合差。监控指标里把这三样都采上比只盯一个秒数可靠得多。下面是生产环境常驻的 8 条核心指标推荐作为指标字典的第一版清单指标名exporter 输出含义单位参考阈值mysql_global_status_queries累计查询数次无看趋势mysql_global_status_threads_running执行中的线程数线程数CPU 核数 × 2mysql_global_status_threads_connected已建立连接数连接数max_connections × 80%mysql_global_status_slow_queries慢查询累计值次基线 p95mysql_global_status_aborted_connects中断连接累计值次持续增长即告警mysql_global_variables_max_connections最大连接数连接数只作分母mysql_global_status_innodb_buffer_pool_reads从磁盘读页累计值页持续增长需关注mysql_slave_status_seconds_behind_master复制落后秒数秒 30 告警这 8 条覆盖了吞吐、连接、存储、复制四个平面每条都有明确的采集来源和对应的处置动作。先跑通这 8 条再往细处扩展比一开始就接十几个 exporter 要好维护得多。3. 用 mysqld_exporter 把数据库监测指标采成时间序列3.1 最小的采集拓扑数据库监测指标落地最常用的是三件套Prometheus 负责抓取和计算告警mysqld_exporter 负责把 SHOW GLOBAL STATUS 这类只读命令转成指标node_exporter 补上 CPU、内存、磁盘。之所以这样拆是为了排错时能直接区分是数据库本身的问题还是主机资源的问题。碰到慢查询先看 node_exporter 的磁盘 await 有没有抬头再看 threads_running 和慢查询数两步就能把问题定位到层。3.2 先建一个最小权限的采集账号mysqld_exporter 不要用 root 连库。常见做法是单独建一个只读账号CREATE USER exporter127.0.0.1 IDENTIFIED BY readonly_pass WITH MAX_USER_CONNECTIONS 3; GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO exporter127.0.0.1;PROCESS 权限用于读取 processlist 和线程状态REPLICATION CLIENT 用于读取主从状态SELECT 用于查询各类状态表。WITH MAX_USER_CONNECTIONS 3 是防止监控账号连接失控把监控自己变成故障源。授权之后不用 FLUSH PRIVILEGESGRANT 语句本身会即时生效。3.3 用 my.cnf 启动 exporter避免密码进进程参数启动参数里直接写 -p 密码会在 ps 输出里暴露不建议这么做。常见做法是准备一个 my.cnf[client] userexporter passwordreadonly_pass host127.0.0.1 port3306然后启动./mysqld_exporter \ --config.my-cnf/etc/mysqld_exporter/.my.cnf \ --collect.info_schema.processlist \ --collect.slave_status \ --web.listen-address0.0.0.0:9104几个参数说明--collect.info_schema.processlist 会查询 information_schema.processlist让 active 线程数更精确但这个查询本身在高并发下锁开销变大低配实例可以去掉--collect.slave_status 只有从库需要开--web.listen-address 如果 Prometheus 不在同一台机器要同步调整防火墙放行。my.cnf 建议属主改成 root:mysqld_exporter权限 640防止其他账号读走密码。提示mysqld_exporter 的 --collect.info_schema.processlist 在连接数上千的实例上会放大锁开销低配实例建议先关掉观察一周再决定要不要开。3.4 exporter 采不到的指标用 textfile collector 补mysqld_exporter 只管 MySQL 自身的状态量业务相关指标比如当前活跃事务数执行超过 5 秒的实时 SQL 条数它不会给你。常见做法是写一个 cron 脚本把结果输出到 node_exporter 的 textfile 目录#!/usr/bin/env bash # /usr/local/bin/mysql_custom_metrics.sh OUT_DIR/var/lib/node_exporter/textfile OUT_FILE$OUT_DIR/mysql_custom.prom TMP_FILE$OUT_FILE.$$ MYSQL(mysql --defaults-extra-file/etc/mysql/metrics.cnf -N -B) active_tx$(${MYSQL[]} -e SELECT COUNT(*) FROM information_schema.innodb_trx 2/dev/null) long_running$(${MYSQL[]} -e SELECT COUNT(*) FROM information_schema.processlist WHERE INFO IS NOT NULL AND TIME 5 2/dev/null) { echo mysql_active_transactions $active_tx echo mysql_long_running_queries $long_running } $TMP_FILE mv $TMP_FILE $OUT_FILE配套 cron 每 60 秒执行一次* * * * * /usr/local/bin/mysql_custom_metrics.sh这个脚本有几个细节值得说明。一是先写临时文件再 mv避免 Prometheus 抓到写了一半的 .prom 文件二是用 --defaults-extra-file 代替命令行传密码ps 里看不到凭据三是指标名统一用 mysql_ 前缀划分命名空间。information_schema.innodb_trx 在长事务多的时候查询本身有开销采集间隔不要低于 60 秒业务高峰期如果发现监控账号占用连接优先降这个频率而不是删指标。3.5 抓取间隔和实例数量的取舍Prometheus 的 scrape_interval 常规设 15 秒就够覆盖告警场景。不要为了实时设成 5 秒——每次抓取 mysqld_exporter 都要执行一批 SHOW 命令5 秒间隔等于给数据库加了一路持续的只读负载。多实例情况下我一般一台实例对应一个 exporter 进程用不同端口区分靠单个 exporter 频繁切换 DSN 的做法会增加连接建立和状态刷新的开销得不偿失。4. 阈值设计数据库监测指标从有数到会用4.1 先跑基线再定阈值监控采上来第一周不要急着配告警。先让数据积累 7 天用分位数算基线quantile_over_time(0.95, mysql_global_status_threads_running[7d])quantile_over_time 把 7 天内的 threads_running 按 95 分位收敛成一个值代表绝大多数时间线程数不超过多少。用 p95 而不是平均值或最大值是因为平均值会被空闲时段拉低最大值会被一次性毛刺拉高。更细致的做法是按星期几分段周一的流量形态和周末凌晨完全不一样共用一套阈值必然误报或漏报。4.2 告警规则怎么写才不吵阈值定好之后写成 Prometheus 规则文件。下面是一组可以改改就用的规则groups: - name: mysql_health rules: - alert: MySQLConnectionRatioHigh expr: | mysql_global_status_threads_connected / on (instance) mysql_global_variables_max_connections 0.8 for: 3m labels: severity: warning annotations: summary: MySQL 连接使用率超过 80% description: {{ $labels.instance }} 连接使用率 {{ $value | humanizePercentage }} - alert: MySQLThreadsRunningHigh expr: mysql_global_status_threads_running 60 for: 5m labels: severity: critical annotations: summary: MySQL 活跃线程持续高位 description: {{ $labels.instance }} threads_running{{ $value }}请检查慢查询与锁等待这条规则的三个要点expr 里的 / on (instance) 是显式指定按实例标签做除法因为连接数和最大连接数两个指标的标签集合不一定一致for: 3m 或 5m 表示持续这么久才触发用来过滤秒级毛刺annotations 里务必带 {{ $labels.instance }} 和 {{ $value }}否则收到告警还要自己查是哪台机器。MySQLThreadsRunningHigh 的 60 只是初始值拿到 7 天基线后改成基线 p95 的 1.5 倍更合理。4.3 组合条件、级别和收敛单一指标的告警往往不可靠。复制延迟高但数据库整体很闲和复制延迟高同时线程数打满是两个完全不同的故障等级处置优先级也不一样。用 AND 把条件组合起来expr: | mysql_slave_status_seconds_behind_master 30 AND ON (instance) mysql_global_status_threads_running 30两个条件同时满足才触发 critical比单指标告警更有决策价值。告警收敛交给 Alertmanagergroup_wait 设 30s、group_interval 设 5m、repeat_interval 设 4h能有效避免一次抖动刷屏。注意恢复通知是 Prometheus 在条件不再满足时自动发出的如果不想凌晨被恢复通知吵醒在 Alertmanager 路由里按时间过滤即可。下表是生产环境常用的初始阈值配合基线数据再微调指标阈值持续时长级别连接使用率 80%3mwarning连接使用率 90%3mcriticalthreads_running基线 p95 × 1.55mwarning复制延迟 30s1mcritical慢查询数基线 p95 × 1.510mwarningbuffer pool 命中率 99%15mwarning磁盘使用率 85%10mcritical注意告警阈值不要照抄任何一篇文章先用自己环境的 7 天数据算基线。buffer pool 命中率这条在低配实例上尤其明显内存紧张时命中率常在 97% 上下波动直接套 99% 会天天误报。5. 把数据库监测指标沉淀成一份可校验的文档5.1 指标字典的六个必填字段回到标题里那个数据库监测指标的文档。一份能长期用的指标文档不该是截图集锦而是一张字段完整的指标字典。我一般每个指标至少写六列指标名、采集来源、单位、计算方法、阈值与级别、负责人。指标名必须和 exporter 输出严格一致PromQL 表达式直接写进去接手的人不用再去 Grafana 面板里翻表达式。5.2 用脚本校验文档和生产环境一致exporter 升级、MySQL 大版本变更都会让指标名静默失效。文档写得再漂亮指标不存在就没有意义。常见做法是跑一个校验脚本用文档里的指标名和 Prometheus 的元数据做差集#!/usr/bin/env python3 import requests prometheus http://127.0.0.1:9090 dict_file mysql_metrics_dict.csv with open(dict_file, encodingutf-8) as f: doc_metrics [r.split(,)[0].strip() for r in f if r.strip()] names requests.get(f{prometheus}/api/v1/label/__name__/values, timeout5).json()[data] missing [m for m in doc_metrics if m not in set(names)] print(缺失指标:, missing if missing else 无)这个接口一次请求返回全量指标名脚本跑完 5 秒内能告诉你有多少条文档指标在 Prometheus 里不存在。配合 cron 每周执行一次exporter 升级时就不会把失效指标带进告警链路。5.3 把采集副作用写进文档指标字典里最容易被忽略、排错时最值钱的一列是采集副作用。information_schema.innodb_trx 的 COUNT(*) 在长事务多的时候会加剧元数据锁竞争processlist 查询在连接数高位时会放大锁开销textfile 脚本里那条超过 5 秒的 SQL 统计也一样。把这些副作用写成一句备注附在对应指标后面下一任维护者调节采集频率就有据可依不用重新踩一遍坑。本文还有配套的精品资源点击获取
返回列表