写这篇东西的动机,是我最近又双叒被拉去救火:一个朋友的业务凌晨两点挂了,数据库连接数打满,登录上去一看慢查询几百条,主从延时大几千秒。最尴尬的是,他们连最基本的监控都没有,全靠半夜爬起来手动看。MySQL 5.7版本虽然已经是上一代产物,但存量依然大得吓人,很多中小团队的核心业务还在它上面跑,监控这件事做没做,应对故障时完全是两种体验。这篇文章就基于我自己在生产环境跑的方案,把MySQL 5.7监控从指标选型、组件选型、部署实操到告警配置、真实排障过程完整过一遍,给还在维护5.7的兄弟团队一个可以直接抄作业的参考。
1. 监控MySQL 5.7真正要看的指标有哪些
很多人一说监控就急着装工具,装完发现面板上全是指标,但根本不知道看哪个。监控的第一步不是部署,而是把指标讲明白。MySQL 5.7没有8.0那么多花哨的performance_schema新特性,监控数据主要靠SHOW GLOBAL STATUS、SHOW GLOBAL VARIABLES、SHOW ENGINE INNODB STATUS和SHOW SLAVE STATUS这几条命令输出。mysqld_exporter会把这些结果自动转成prometheus格式,这是后话,前提是你得先知道哪些指标值得天天盯着。
1.1 连接与并发:最容易触顶的第一道墙
数据库挂了,80%的情况首先表现为连接数异常。5.7版本默认max_connections只有151,对生产环境来说非常保守,很多人上来就调成500甚至1000,但改大不解决根本问题,反而会把压力转移到数据库进程本身。
核心要关注的指标是这三个:
Threads_connected:当前活跃连接数,直接反映连接水位。Max_connections:连接上限,配置值。Connection_errors_max_connections:累计因超出连接上限被拒绝的次数。
我一般用Threads_connected / Max_connections这个比值做告警判断,超过80%就要准备扩连接或排查连接泄漏了,超过90%基本属于危险区域。真实环境里,比值跳到85%以上通常不是容量不够,而是业务代码连接池有问题,或者慢查询把连接全占住了。
1.2 查询吞吐与慢查询:业务的体温计
QPS(TPS)这类指标听起来很大路货,但确实是判断数据库“活着且健康”的基础。mysqld_exporter采集的mysql_global_status_queries是所有语句的累计值,在Prometheus里用rate()函数一算,就能看到实时QPS曲线。再配合Com_select、Com_insert、Com_update、Com_delete看读写分布,能快速判断数据库是在服务业务还是被某类操作拖垮。
慢查询指标则是重中之重。5.7里Slow_queries是累计慢查询数,long_query_time是阈值,默认10秒,生产环境建议调到1秒甚至更低。我见过太多团队10秒还没意识到有问题,等压测或事故来了才发现慢查询日志比业务日志还大。还有一点容易被忽略:log_queries_not_using_indexes这个参数,建议打开,虽然会有额外日志量,但能帮你抓到那些因为缺索引导致的全表扫描。
1.3 InnoDB缓冲区与存储引擎指标
5.7默认存储引擎是InnoDB,它的性能直接决定数据库整体表现。最核心的是buffer pool相关指标:
Innodb_buffer_pool_read_requests:逻辑读次数。Innodb_buffer_pool_reads:物理读次数,即需要从磁盘读取的次数。
命中率计算公式是(read_requests - reads) / read_requests。正常情况下这个值应该在95%以上,低于90%就得认真怀疑innodb_buffer_pool_size配置不合理或者SQL设计有问题了。实际调优时,5.7推荐把buffer pool设置到物理内存的50%到70%,但这需要根据数据热度和服务器内存量动态调整,不是拍脑袋定的。
另外几个值得盯的InnoDB指标:
Innodb_row_lock_waits:行锁等待次数,飙升说明存在锁竞争。Innodb_row_lock_current_waits:当前正在等待的行锁数量。Innodb_log_waits:日志缓冲区等待次数,频繁出现说明提交事务过于密集,需要检查innodb_log_buffer_size。
这些指标在故障初期可能不算明显,但配合慢查询和连接数一起看,通常能拼出完整的问题图谱。
1.4 主从复制链路:沉默的故障源头
主从复制是MySQL高可用方案的地基,但复制链路恰恰最容易“静默故障”。5.7里最经典的监控指标来自SHOW SLAVE STATUS:
Slave_IO_Running:IO线程是否在跑,负责从主库拉取binlog。Slave_SQL_Running:SQL线程是否在跑,负责执行中继日志。Seconds_Behind_Master:从库落后主库的时间。
Seconds_Behind_Master这个指标要辩证看:它是从库SQL线程执行时间与IO线程读入时间的差值,只能反映一个粗略趋势。在5.7的多线程复制(MTS)模式下,这个值有时候会不准确,甚至可能出现主键冲突报错后归零的情况。所以复制监控不能只看延迟,还要盯Slave_IO_Running和Slave_SQL_Running的运行状态,以及Last_IO_Errno、Last_SQL_Errno是否有非零值。如果用了GTID,还要关注gtid_executed是否有推进。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 监控体系的技术选型:为什么是Prometheus这套组合
MySQL监控的可行方案很多,老牌的Zabbix至今还有大量用户,那为什么我最终选了Prometheus + mysqld_exporter + Grafana + Alertmanager这套组合?主要原因有几个。
一是数据模型适合时序类监控。Prometheus的拉取模式对MySQL这种需要跨实例统一采集的场景非常友好,只要给每个实例部署一个exporter,Prometheus配置里加一行target就能接入,横向扩展几乎没有成本。Zabbix这种传统方案不是不好,但在指标灵活定义、告警规则编写、和现有云原生环境打通这几方面,确实不如Prometheus生态顺手。
二是Grafana的图表生态成熟。MySQL监控仪表盘模板非常多,导入即用,社区维护得也勤快,省去了从零画图表的功夫。对比Zabbix自带的图形界面,Grafana在交互和美观度上领先太多,管理层要看状态,开发要看趋势,都能直接满足。
三是告警规则用PromQL表达非常灵活。比如“连接数超过上限的85%持续5分钟”这种规则,写起来就是一行表达式,不像传统监控软件要四处点选配置。
这套系统里组件分工很明确:
| 组件 | 职责 |
|---|---|
| mysqld_exporter | 部署在数据库主机,采集MySQL指标并暴露成Prometheus格式 |
| node-exporter | 部署在数据库主机,采集CPU、内存、磁盘、网络等操作系统指标 |
| Prometheus | 负责拉取所有exporter指标,存储时序数据,执行告警规则 |
| Alertmanager | 接收Prometheus触发的告警,负责去重、分组、路由到钉钉/飞书/邮件 |
| Grafana | 读取Prometheus数据做可视化展示 |
很多人在做MySQL监控时会漏掉node-exporter,这是个大坑。数据库出问题往往先是操作系统层面出问题:磁盘满了、CPU打满、swap波动、网络丢包,这些不会体现在Threads_connected上,但都会间接拖垮数据库。node-exporter部署成本极低,建议和mysqld_exporter一起装上。
3. mysqld_exporter部署与配置实操
这里把部署环节的关键步骤拆开讲。网上很多教程直接让你下载二进制跑起来完事,但真正上线的时候,账号权限、启动方式、抓取间隔这些细节都会变成坑。
3.1 创建监控专用账号与最小权限
mysqld_exporter需要一个MySQL账号去执行状态查询。这里最忌讳用root或拥有全局写权限的账号,一个监控账号只需要只读权限。我习惯单独建一个专用账号,权限如下:
sql复制CREATE USER 'mysqld_exporter'@'127.0.0.1' IDENTIFIED BY '你的强密码';
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'mysqld_exporter'@'127.0.0.1';
FLUSH PRIVILEGES;
这几个权限的含义:
PROCESS:允许执行SHOW ENGINE INNODB STATUS、SHOW PROCESSLIST,以及访问performance_schema.processlist。REPLICATION CLIENT:允许执行SHOW SLAVE STATUS和SHOW MASTER STATUS,这是采集主从复制状态的基础。SELECT:允许执行SHOW GLOBAL STATUS、SHOW GLOBAL VARIABLES等只读查询。
提示:如果后续mysqld_exporter某些指标取不到值,十有八九是权限不足,而不是部署问题。检查一下账号授权是否完整即可。
3.2 安装并启动mysqld_exporter
mysqld_exporter可以从GitHub的prometheus-community仓库下载二进制包。下载后解压到/usr/local/mysqld_exporter目录,准备一个配置文件,保存数据库连接信息:
ini复制[client]
host=127.0.0.1
port=3306
user=mysqld_exporter
password=你的强密码
这里有个细节:把密码放在配置文件里比直接写在命令行参数要安全,因为命令行进程列表可能被其他用户看到。
启动方式建议用systemd管理,方便设置开机自启和崩溃重启。unit文件可以这样写:
ini复制[Unit]
Description=Prometheus MySQL Exporter
After=network.target
[Service]
Type=simple
User=prometheus
Group=prometheus
ExecStart=/usr/local/mysqld_exporter/mysqld_exporter \
--config.my-cnf=/etc/mysqld_exporter.my.cnf \
--web.listen-address=:9104
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
启动后验证一下:
bash复制systemctl daemon-reload
systemctl enable mysqld_exporter
systemctl start mysqld_exporter
curl http://127.0.0.1:9104/metrics | head -20
能输出一堆mysql开头的指标就说明正常了。
3.3 Prometheus采集任务配置
Prometheus要拉取mysqld_exporter的指标,需要在prometheus.yml里加一个job:
yaml复制scrape_configs:
- job_name: "mysql57"
static_configs:
- targets: ["10.0.0.11:9104"]
metrics_path: "/metrics"
scrape_interval: 15s
抓取间隔我建议15秒。太短会对数据库主机产生额外的连接开销,太长会导致告警反应慢。15秒对绝大多数生产环境是平衡点。如果你有多台MySQL实例,就把targets列表往后追加。
3.4 Grafana仪表盘导入与自定义
Grafana装好之后,直接通过“Dashboards -> Import”导入社区模板。网上流传比较广的MySQL仪表盘ID有7362、10000等,导入后能直接看到连接数、QPS、InnoDB状态、复制状态等面板。不过我用下来发现,这些模板有的指标名停留在旧版本exporter命名上,和当前版mysqld_exporter会有个别字段对不上,需要自己微调。
对5.7版本,我通常会在模板基础上补三个面板:连接水位百分比、buffer pool命中率、从库复制延迟。这三个面板对运维判断最有价值。添加面板时查询语句直接写PromQL就行,比如连接水位:
promql复制mysql_global_status_threads_connected / mysql_global_variables_max_connections
4. 告警规则:把问题拦在用户发现之前
监控的意义不在于“事后能查到数据”,而在于“问题发生前能收到通知”。告警规则设计得好不好,直接决定这套系统有没有实际价值。
4.1 连接数告警
连接数告警是我在生产环境用频率最高的一条规则。表达式可以这样写:
yaml复制groups:
- name: mysql57-alert.rules
rules:
- alert: MySQLThreadsConnectedHigh
expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "MySQL连接数过高"
description: "实例 {{ $labels.instance }} 连接数已占上限的85%,持续5分钟"
for: 5m这个参数的意思是“阈值持续5分钟才触发告警”,目的是过滤掉短时峰值。比如业务秒杀场景连接数会瞬间冲高,但马上回落,这种就不该报警。设了持续时长能减少很多误报。
4.2 主从复制延迟告警
复制延迟对业务的影响是累积的,延迟越高,数据丢失风险越大。告警阈值要根据业务容忍度来定,我的默认值是30秒,超过2分钟就转严重:
yaml复制 - alert: MySQLSlaveReplicationLag
expr: mysql_slave_status_seconds_behind_master > 30
for: 2m
labels:
severity: critical
annotations:
summary: "MySQL主从复制延迟"
description: "从库 {{ $labels.instance }} 复制延迟已超30秒,当前值 {{ $value }}"
注意:mysqld_exporter在部分版本中,如果IO线程和SQL线程都是Running,
mysql_slave_status_seconds_behind_master才会有值;如果复制已中断,这个指标通常不会上报,需要额外添加对mysql_slave_status_slave_io_running的状态检查。
4.3 磁盘空间与InnoDB告警
磁盘空间必须靠node-exporter的指标来做告警,因为mysqld_exporter拿不到文件系统层面的数据。我通常对MySQL数据目录所在分区单独设一条规则:
yaml复制 - alert: MySQLDataDiskSpaceLow
expr: (1 - node_filesystem_avail_bytes{mountpoint="/data"} / node_filesystem_size_bytes{mountpoint="/data"}) * 100 > 90
for: 10m
labels:
severity: critical
annotations:
summary: "MySQL数据盘使用率超过90%"
description: "分区 /data 使用率已达 {{ $value | humanizePercentage }}"
InnoDB相关的告警我一般不是每个指标都配,而是优先配置行锁等待和buffer pool命中率。命中率低于90%持续15分钟,基本可以断定buffer pool配置有问题或SQL有大面积全表扫描,值得立刻排查。
4.4 告警通道怎么接
Prometheus本身只负责计算和触发,真正把告警推给人是Alertmanager的工作。Alertmanager支持非常多的Receiver,包括邮件、钉钉、飞书、企业微信、webhook等。
我目前最常用的是钉钉群机器人。在钉钉群添加一个自定义机器人,拿到webhook地址,然后在Alertmanager配置文件里加一个webhook receiver:
yaml复制receivers:
- name: "dingtalk"
webhook_configs:
- url: "https://oapi.dingtalk.com/robot/send?access_token=你的token"
send_resolved: true
send_resolved: true表示告警恢复时也推送一条告警解除通知,这个对值班人员来说很重要,不然一个问题要人工再去确认一遍是否恢复。
5. 一次真实监控排障:连接数飙升背后的问题
纸上谈兵讲了这么多,分享一个真实案例,完整还原我是怎么用监控数据定位问题的。这套逻辑你可以直接套用到自己的MySQL环境里。
5.1 现象:告警先来,业务后挂
有一天下午我正开会,手机弹出一条Alertmanager推送:“MySQL连接数过高,实例10.0.0.11连接数已占上限的85%,持续5分钟”。业务方还没报障,但监控已经先报警了。我打开Grafana,先看了连接水位曲线,发现几乎是在10分钟内从30%直线拉到85%,这个斜率非常异常。
5.2 用监控数据逐层定位
看到连接数飙升,我的第一反应不是去数据库上做show processlist(虽然那也会做),而是先把监控面板摊开。
第一步看QPS曲线。连接数和QPS同时暴涨,说明有大量请求打进来。但如果QPS涨幅不大,连接数却暴涨,那就要怀疑连接被“挂着”没释放。
第二步看慢查询曲线。结果发现Slow_queries从每分钟个位数飙升到每分钟三百多,我一下就锁定了方向:不是连接池泄漏,而是慢SQL把连接拖住了,每个请求进来之后都要等SQL返回,新的请求只能继续排队,连接数自然被顶满。
第三步定位慢查询来源。登录MySQL执行SHOW FULL PROCESSLIST,发现大量会话卡在同一条查询上,状态是Sending data。从查询语句看,是在扫描一个千万级的大表,关联了三个子查询,明显缺少合适的索引。
第三部同样重要——看InnoDB行锁指标,发现Innodb_row_lock_current_waits也飙到几十,说明这个慢查询还和别的事务存在锁竞争,进一步加剧了阻塞。
5.3 修复与验证
定位到问题类型后,修复反而没那么难了。我跟业务同事确认这条SQL是不是新上线的逻辑,确认后立刻做了两件事:
第一,临时终止那些反复执行的慢查询会话,把连接数压力先降下来。这属于应急止损,不能拖。
第二,让开发同事优化SQL并补上联合索引。索引加上后,那条SQL的执行时间从几秒降到几十毫秒。
验证阶段我全程盯着Prometheus面板。慢查询曲线在几分钟内回落到正常水位,连接数也跟着下降到30%以下,整个过程中业务只是短暂卡顿,没有完全挂掉。
这个案例里最值得说的不是修复SQL本身,而是监控系统在整个过程中扮演的角色:如果没有连接数告警,这张工单可能在业务彻底瘫痪后才会被提交。而有了监控之后,我在业务方感知到问题之前就已经定位到了根因。
6. 监控体系上线后的日常维护建议
最后再说几点日常运维里的体会。监控系统不是搭完就一劳永逸了,它本身也是需要维护和调优的资产。
第一,数据保留周期要提前规划。Prometheus默认本地存储,如果采集频率是15秒,一天下来的数据量对磁盘还是有一定压力的。我一般给Prometheus数据盘单独挂一块大容量磁盘,数据保留90天,超过90天直接走官方推荐的持久化方案或仅保留聚合数据。不然时间一长,Prometheus所在磁盘满了,监控本身先挂了,那就尴尬了。
第二,告警规则要定期review。业务是动态的,连接数、慢查询、延迟的正常水位也会随着业务变化而变化。比如刚上线时连接数30%算正常,几个月后业务量翻倍,80%可能才是常态。这时候不调阈值,系统会天天报警,值班团队最后疲劳无视,真正出大事反而没人管。我一般每季度手动review一次阈值,结合历史数据把误报率压下去。
第三,mysqld_exporter不要随意升级。看到新版本出来就手痒升级,结果PromQL查询字段变了,仪表盘面板全部显示No Data,这种事我见过不少。当前跑着没问题就保持版本稳定,升级前一定要看release note里的breaking changes。
第四,监控数据可以用来做容量规划。比如定期导出连接水位和QPS的同比数据,提前评估是否要扩容、是否要调整buffer pool、是否需要引入读写分离,这些都是监控埋点带给你的额外价值。
我在实际维护中还有一个习惯:每个月挑一天,翻一遍Prometheus告警历史,把那些“发生过但不该再发生”的告警拿出来复盘,看看问题闭环没有。这比单纯盯着面板看趋势有用得多,因为监控最终的回报不是图表好不好看,而是故障时能帮你省下多少排查时间。
