1. 为什么要用Prometheus监控MySQL:从一次事故说起
刚接手这套系统的时候,MySQL实例已经有几十台在跑,分布在不同的业务线里。某天业务方半夜打电话过来说批量任务全挂了,排查到最后才发现是某台从库的磁盘早就满了,主库的binlog同步中断了好几个小时没被发现。那时候监控全靠人肉巡检脚本定时去查数据库状态,慢、延迟高,而且没有人会半夜盯着告警看。那次事故之后,我才决定把整个数据库监控体系从零搭起来。
当时摆在面前的选项其实不少,Zabbix、Nagios、InfluxDB那套都有各自的拥趸。最终选定了Prometheus + mysqld_exporter + Grafana的组合,核心原因是这套方案在云原生时代太顺了。Prometheus 天然支持多维数据模型,指标抓取用Pull模式,MySQL的状态变量、InnoDB 指标、复制状态这些都能量化成一个又一个的 gauge 和 counter,配合Grafana做可视化,数据从采集到展示的链路非常短。对于已经跑着 Kubernetes 和微服务的团队来说,监控MySQL只是Prometheus生态的一部分,后续要扩展监控应用、资源、中间件,完全是同一套基础设施,不会出现监控孤岛。
这篇内容适合谁看?如果你是刚从脚本巡检升级到专业监控体系的运维工程师,或者被分到了数据库稳定性相关的活,又或者你只是想把 MySQL 的指标好好用起来,这套方案都可以直接照着抄。我会把部署过程、配置细节、告警规则、可视化面板,以及踩过的坑和排查思路全部记录下来,尽量做到拿来就能用、照着就能跑。
2. 监控架构设计:先理清楚数据怎么流动
2.1 核心组件和数据流
整套监控链路涉及四个核心角色,先画清楚它们各自负责什么,后面配置的时候才不会被绕晕。
Prometheus 服务端:负责定时拉取指标、存储时序数据、执行告警规则。它会按配置的 interval 主动访问 exporter 暴露的 HTTP 接口,拿到监控数据后打上时间戳存入本地时序库。
mysqld_exporter:Prometheus 和 MySQL 之间的翻译官。它用我们创建的监控账号连接目标 MySQL,通过执行 SHOW GLOBAL STATUS、SHOW GLOBAL VARIABLES、SHOW MASTER STATUS、SHOW SLAVE STATUS 等 SQL 取到数据库内部的状态快照,再转换成 Prometheus 的时间序列格式,在 9104 端口上暴露出来。默认的采集器并不算多,很多重要指标依赖额外开启的 collector。
Grafana:负责把时序数据变成看得懂的图表和仪表盘。它不直接对接 MySQL,而是通过 Prometheus 的数据源查询 PromQL 表达式,渲染成曲线图、热力图、表格等各种形式,同时也可以在上面配置告警通知。
Alertmanager:Prometheus 生态里的告警中枢。Prometheus 本身只负责判断告警规则是否触发,真正负责去重、分组、静默,以及通过 webhook、邮件、企业微信、钉钉等方式通知人的,是 Alertmanager。
数据流向是单向且清晰的:mysqld_exporter 从 MySQL 取指标,Prometheus 从 mysqld_exporter 拉指标,Grafana 和 Alertmanager 从 Prometheus 读取指标和触发状态。这个单向链路让问题排查变得简单:哪个环节没数据,顺着链路查就行,不需要到处臆测。
2.2 为什么必须用独立的监控账号
直接拿 root 账号给 exporter 用是最常见的坑,这个我一开始也干过。虽然配置简单,但风险非常大:监控脚本挂了不会出问题,但如果 exporter 存在漏洞被攻击,数据库权限就被直接拿走了。更麻烦的是,root 账号在 binlog 里留下的操作记录会干扰审计排查。
正确的做法是创建最小权限的专用账号,这也是整套方案里我认为优先级最高的安全设计。给监控账号分配的权限只需要 PROCESS、REPLICATION CLIENT 和 SELECT,分别用于查看线程状态和服务器变量、查看复制信息、读取状态变量。这里有一行配置值得提醒:给远程访问设置网段限制,避免监控账号被公网任意主机探测。
在 MySQL 8.0 中,创建监控账号的语句和管理员账号的权限体系有一些变化,需要留意默认认证方式对客户端兼容性的影响。exporter 通过 TCP 方式连接时,需要配套的加密与认证选项配置,老旧版本可能不兼容新的默认认证插件,连接不上时优先从认证方式排查。
在后续配置 exporter 时,不要图省事把连接密码直接明文写在命令行参数里。文件方式加载凭证配合合理的文件权限,能避免进程列表暴露密码的风险,命令行的信息容易被同主机的其他用户看到。
3. 部署实操:从零搭建一套能用的监控环境
3.1 环境准备与Prometheus部署
先说我这边的环境,供你参考:操作系统用的是 Linux 发行版,MySQL 跑了 5.7 和 8.0 两个版本,Prometheus 和 Grafana 都部署在独立的服务器上。实际部署时版本选择建议遵循一个原则:Prometheus、exporter、Grafana 尽量选择官方最新稳定版,而不是随便 apt install 或 yum install 拿到的版本,仓库里的包往往落后好几个大版本。
二进制部署是兼容性最好的方式,也最容易理解组件之间的关系。Prometheus 本身就是单个二进制文件加一个目录,不依赖额外的运行环境。官方下载页面拿到 tarball 后,解压出来就是可用的服务端程序。特别说明一下存储目录:prometheus.yml 是主配置文件,data 目录存放时序数据,控制台和库接口都通过 HTTP 暴露。启动时指定配置文件路径,后续把 service 文件写好交给 systemd 管理就行。
注意:data 目录务必放在磁盘空间充足且独立的分区,如果和系统目录共用一块且空间很小,时序数据写满后不止监控不可用,整个服务器都可能被拖垮。我见过不止一次因为监控服务器磁盘被时序数据撑爆导致主机卡死的案例。
再有就是启动参数中的监听地址问题。如果这台 Prometheus 后续还要被其他区域的监控数据汇入,或者办公网要直接访问查询页面,就要把默认的 localhost 换成可访问的地址,或者通过反向代理控制访问。生产环境建议不要图方便把监听地址直接裸奔到公网,Prometheus 自带的 web 控制台没有认证能力。
3.2 监控账号的创建:把权限给得刚刚好
监控账号的创建要在每台被监控的 MySQL 实例上都执行一遍。为了批量操作方便,我写了个脚本用循环去执行,但脚本本身不复杂,核心就是下面这段 SQL,我拆开来解释每一行的意图。
CREATE USER 'mysqld_exporter'@'192.168.1.%' IDENTIFIED BY 'StrongPassword123'; GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'mysqld_exporter'@'192.168.1.%'; FLUSH PRIVILEGES;第一行创建用户并限制来源网段,IDENTIFIED BY 后面是密码。这里主机部分是重点:写 192.168.1.% 意味着只有这个网段的机器能连,exporter 部署在什么网段,这里就写什么网段。第二行是授权,库表范围写.是因为状态变量和复制信息不属于某个具体库,必须全局权限才能读取。第三行刷新权限让修改立即生效。
如果你用的是 MySQL 8.0,注意默认的认证插件是 caching_sha2_password。某些版本较低的客户端或者驱动连接时可能报认证插件不支持,那时候要么升级连接库,要么在创建用户时显式指定 mysql_native_password,不过后者会降低安全级别,升级客户端才是更合理的做法。
权限验证千万别跳过,我强烈建议建完账号后用下面的命令手动连一次,确认权限够用且能拿到关键信息。这一步做好,后面排查 exporter 连接问题的时候能省掉一大半的猜测。
mysql -h 192.168.1.10 -u mysqld_exporter -p -e "SHOW GLOBAL STATUS; SHOW VARIABLES LIKE 'server_uuid';"如果这条命令执行正常,能看到一大串状态变量和配置项,说明账号权限没问题。如果报 Access denied 或 PROCESS 权限拒绝,回到上一步仔细检查授权语句。
3.3 mysqld_exporter 的安装与配置:几个容易踩的坑
exporter 部署位置其实有讲究。理想情况下应该部署在 MySQL 实例所在的机器上,这样连接走 socket 或者回环地址即可,不经过网络层,安全性和稳定性都更好。如果实例太多没法一台台部署,也可以集中部署然后通过网络连接,但每新增一台实例就要同步改配置,维护成本会更高。我的做法是跟随实例部署,一台 MySQL 配一个 exporter,本地以 systemd 服务方式托管。
exporter 的运行方式并不复杂,二进制文件加了几个必选参数。我用的是通过文件方式加载数据库凭证,避免命令行暴露密码。需要预先创建一个包含用户名和密码的配置文件,然后 exe 启动时通过参数指定这个文件的位置,同时再指定数据源名称。这里要求 MySQL 账号密码写在 DSN 中,整体结构类似 URL 的格式。exporter 本身通过参数指定监听端口,默认是 9104。
服务托管方面,写一个 systemd service 文件,指定用户、启动命令,再加上 service 文件本身的管理和重载步骤。需要注意 exporter 用什么系统用户跑,一般建议不要用 root,而是单独建一个低权限用户,比如 prometheus。服务配置里再设置进程重启策略,让 exporter 崩溃后自动拉起。
配置完后用一个临时命令验证指标接口是否正常工作。从接口返回的信息中过滤一下关键词,如果能看到很多以 mysql_ 开头的指标,说明采集器工作正常,数据也已经成功暴露出来了。端口连通性也要确认,否则 Prometheus 拉不到数据时无法判断是端口没有监听,还是被防火墙挡住了。
提示:mysqld_exporter 默认只开启部分采集器。如果只按默认配置跑,你会拿到一部分基础指标,但 InnoDB 相关的重要指标是缺失的。配置文件中通过 collector 开关来控制采集项,需要什么指标就开对应的采集器,一个经典的收集场景是开启 InnoDB、status 和 variables 这几个核心项。特别是处理复杂 SQL 的业务库,innodb 相关的缓冲池命中率、锁等待、日志写入情况这些才是排查性能问题的关键数据。
3.4 Prometheus 接入与指标验证:确认数据真的拉过来了
Prometheus 拉取目标是在主配置文件中的 scrape_configs 段定义的。新增加一个 job 来描述 MySQL 监控目标的采集方式,并指定目标的地址和端口。这里每个目标地址就是部署了 exporter 的那台机器,整体配置并不复杂。
配置完成后需要先校验配置文件语法,Prometheus 专门提供了命令行检查工具,直接传入配置文件即可。如果没有输出错误信息,说明配置语法是正确的。接下来重新加载配置,让新增加的抓取任务生效。需要注意的坑是:Prometheus 默认的抓取超时时间和间隔,如果 MySQL 实例很多或者单实例指标量大,一个抓取周期内可能拉不完,需要在全局配置或 job 级别调整这些时间参数。我当时几十个实例的抓取用了默认参数,但单台慢查询很多的实例确实出现过抓取超时的情况,只有长时间观察并调整参数后才稳定。
验证拉取是否正常有很直观的办法:在 Prometheus 的查询框里输入一个指标名,点执行后如果能看到数据表和图表,说明数据链路是通的。这里需要特别确认的是:第一,指标有没有值;第二,up 指标有没有达到预期结果。通过表达式查询当前实例的存活状态,只有当值为 1 时,才表明抓取正常。这个验证步骤不能跳,因为它既验证了 Prometheus 到 exporter 的链路,也验证了 exporter 到 MySQL 的链路,是整个监控能否真正发挥作用的前提。
4. 告警规则:让监控从"看板"变成"值班"
4.1 哪些指标值得第一时间盯住
监控面板搭起来之后,很多人会陷入什么都想看的误区,把几十个指标全部堆上去,结果真正的故障反而被海量信息淹没了。我个人的观点是,告警规则宁可少而精,也不要滥而杂。真正值得第一时间盯着看的,就这几类:
实例存活状态:包括进程是否挂了、端口是否通、采集是否正常。这是最底层的兜底,任何异常都得先从这里暴露。
MySQL 存活与连接状态:数据库进程是否存活、当前连接数占最大连接数的比例。连接数打满时通常会伴随雪崩,这个指标要配上高优先级告警。
复制异常:主从复制是否在跑、复制延迟是多少、从库 SQL 线程有没有报错。复制状态异常往往是数据一致性出问题的先兆,也是最容易延迟发现的故障。
慢查询与性能核心指标:慢查询数量的增长速度、InnoDB 缓冲池命中率、临时表落盘数量、锁等待时间。这些指标正常与否直接反映了 SQL 质量和数据库健康度。
磁盘与容量趋势:磁盘使用率、binlog 文件总体积的增长趋势、InnoDB 数据文件大小。磁盘满是非计划停机的大概率原因,而容量问题用百分比阈值很难防住,结合增速预测会可靠很多。
一行关于如何选择阈值的建议:不要照抄网上现成的阈值,你的库和别人的库在硬件规格、业务压力、数据规模上都不一样,只能拿自己正常运营一段时间的数据做基准,然后在这个基准上浮动成阈值。网上那些数字只能作为起点,不能作为终局。
4.2 告警规则文件的结构与写法
Prometheus 的告警规则统一放在规则文件里,通过主配置进行加载。规则文件的核心就是定义规则组,每个规则组包含多条告警规则。告警规则包含告警名称、告警级别、触发条件的 PromQL 表达式、以及告警内容的模板变量。
PromQL 是这套体系里最需要花时间掌握的技能。乍一看有点别扭,但它本质上就是带着过滤器和聚合函数的时序查询语言。比如查询实例是否存活,就是获取指标当前值然后判断是否为 0;判断复制是否中断,就是确认当前主从线程的运行状态值是否为 0。这些都是最基本的用法。复杂一点的查询,比如计算连接数使用率,需要先拿到当前连接数指标,再除以最大连接数配置值,然后乘以 100 得到百分比,实际上就是指标之间的数学运算。PromQL 的聚合操作对多实例场景特别有用,一个表达式可以把所有实例的指标汇总成一条线,一眼看出全局状态。
规则文件里普遍需要慎重对待的是 for 参数。它表示条件持续多久才触发告警,这个参数能避免瞬时抖动造成的误报。比如磁盘使用率超过 85% 持续 5 分钟才触发,比一超过阈值就报警科学得多。for 设置太短会频繁收到噪音告警,值班同事会慢慢忽略;设置太长又会延迟发现真实故障,需要结合业务容忍度来权衡。
告警规则写好后同样需要校验语法,Prometheus 提供了专门的检查命令,返回成功后再向服务端发送重载信号。规则是否生效,可以直接在告警管理页面看到结果。刚开始全配置完后,规则列表里 INACTIVE 状态是正常的,因为条件还都没触发;等真实故障或者自测压测时,状态会从 INACTIVE 跳到 PENDING,再变为 FIRING。
4.3 复制监控的三个核心指标
对 MySQL 来说,复制状态监控是整个监控体系里优先级最高的部分,一旦主从复制出现问题,主库仍然正常对外提供服务,业务感知不到异常,但数据已经在悄悄产生差异。等发现问题时往往已经很晚了。
复制相关的核心指标有三个,我建议无论如何都要配齐。mysqld_exporter 提供的复制状态指标用来判断 IO 线程和 SQL 线程的运行状态,SQL 线程失效时几乎可以断定复制已经中断。通过该指标可以精确区分来源:只有其中一个为 0,就是对应的线程中断;两个线程正常而延迟依然很高,就要看下面的秒级延迟指标。复制源位置位点这个指标用于更精细的延迟判断,它表示从库已经执行到主库哪个 binlog 位置了。实际情况中,主库和从库的位点之间还有差异,这个差异的秒数可以更真实地反映复制延迟。组合使用三个指标才能完整描述复制的健康状态。
我用一个实际的案例来说明为什么只看某一个指标会误判。某次从库的延迟告警频繁触发,查复制状态线程都是正常的,位点也在前进,看起来一切正常。后来抓了两边指标对比,发现从库执行位点和主库当前位点的差距虽然在缓慢缩小,但主库的写入压力太大,新产生的 binlog 比从库消费的速度快得多。这种情况单一指标根本看不出来,三个指标一起看才能还原全貌。
5. 可视化实战:Grafana 里把数据库画成仪表盘
5.1 数据源接入与仪表盘选型
Grafana 本身不存监控数据,它只是把 Prometheus 的数据源接进来做展示。接入数据源的过程实在没什么可说的,数据源类型选择 Prometheus,地址填 Prometheus 的 HTTP 接口地址,保存并测试连通后就可以用了。如果出现连接失败,优先检查网络连通性和地址是否正确,这两步占排查原因的八成以上。
仪表盘这块我需要多说两句。网络上有大量现成的 MySQL Dashboard,建议不要直接导入就用。我的做法是拿一份社区比较流行的 JSON 作为基础模板,然后把不适合自己场景的面板删掉,再补充自己关心的面板,一点一点改造成自己的。完全从零开始画面板其实也不难,但效率不高,基础模板能帮你把变量定义和常用 PromQL 骨架都搭好。
仪表盘里最值得花心思的是变量的使用。在面板查询中写死实例地址的做法会让仪表盘完全失去复用性。正确的做法是在仪表盘设置里定义变量,比如用采集指标的 job 变量和实例变量,然后查询语句用变量去替换写死的部分。这样切换不同的实例或者只看某台机器的数据,都只需要在上方下拉框里操作,根本不需要改动图表。
5.2 核心面板的高价值配置技巧
面板的配置核心在于两点:第一,PromQL 查询要准确反映你想看的指标;第二,图表的展示方式要让人一眼看懂。举几个我实际在用的面板作为参考:
连接数面板。很多模板直接画连接数绝对值,但我更建议画使用率:当前连接数除以最大连接数,再乘以 100,这样所有实例可以在同一张图上对比,有没有接近瓶颈一眼就看得出来。用实例作为图例变量来区分不同机器,某台机器使用率 90% 以上时,图表曲线会直观地顶到天花板位置。
InnoDB 缓冲池命中率面板。命中率高说明大部分数据页都能从内存里拿到,不需要访问磁盘,性能就比较好。这个指标计算公式是命中次数的速率除以总读取次数的速率,再乘以 100。用速率而不是直接拿计数器做除法,是因为计数器是累加值,直接拿当前值相除没有任何意义。这一点是所有带 counter 属性的指标做面板时都适用的原则,必须用 rate 函数先转成速率。
慢查询数量面板。慢查询本身是一个计数器,直接画当前值的意义不大,画它的增速才有价值。用速率函数算出每秒新增多少慢查询,再叠加一个趋势线,业务上线后的慢查询趋势直接决定了要不要安排 SQL 优化。
复制延迟面板。直接把复制状态指标和延迟指标画在一起,用不同的图例颜色区分。主从复制中断时曲线会直接跳到很高的值或者变成空值,配合告警规则,问题一出现就能在面板上看到信号。
实践心得:Grafana 面板不是越多越好,一个仪表盘打开的时候,人的注意力其实只能同时处理有限的曲线。太多了反而不知道该看哪里,我们在实际使用中逐步精简,最终保留下来的核心面板数量并不多,但每一个都能在故障时给出明确信号。
5.3 告警通知:把异常推给真正该知道的人
告警规则触发后,如果不通知人,那和没配告警没有区别。Alertmanager 就是用来打通 Prometheus 和通知渠道的组件。它的核心概念有三个:分组、抑制、静默。分组是把同类告警合并成一条通知,否则一次故障几十台实例同时告警,人手一部手机就不停响;抑制是当某个高优先级告警触发时,自动屏蔽由它衍生的低优先级告警,避免告警风暴;静默用于在已知的维护窗口期内主动屏蔽某些告警。
Alertmanager 接收 Prometheus 推送告警这个事情,在 Prometheus 启动参数中指定 Alertmanager 地址即可。但实际配置时要注意:Prometheus 发送的是告警触发事件到 Alertmanager,由 Alertmanager 根据路由规则决定发送给哪个接收者,再调用相应的通知接口。所以 Alertmanager 的配置文件核心包括路由树、接收者,以及每个接收者下的通知实现细节。比如路由树定义了告警级别为紧急的走某个接收者,级别为普通的走另一个接收者,不同的告警交给不同的人处理,这才是合理的分级策略。
告警通知的接收方式上,我们团队内部用的是企业微信和邮件。实际上 Alertmanager 底层通过 webhook,理论上任何能收 HTTP 请求的接口都能接。配置过程中最大的坑是模板渲染失败,通知内容里如果引用了不存在的告警标签,整条通知会发送失败,或者发出去的内容是空壳。调试告警通知最好直接触发一个真实告警来验证,不要只看 Alertmanager 的控制台显示已发送就完事。
6. 常见问题与排查技巧记录
6.1 一张速查表解决高频问题
下面把我这段时间遇到过的典型问题整理出来。遇到问题时别慌,按表格里的方向逐项排查,大部分问题很快就能定位。
| 现象 | 可能原因 | 排查方向与解决 |
|---|---|---|
| Prometheus 的 up 指标为 0 | exporter 未启动、端口未监听、网络不通 | 检查 exporter 进程、端口、防火墙与抓取配置 |
| exporter 连接 MySQL 报认证错误 | 监控账号密码错误、网段限制、认证插件不兼容 | 手动用 mysql 客户端连接验证,确认权限和认证方式,必要时升级驱动 |
| 部分 mysql_ 指标缺失 | 采集项未开启,exporter 默认配置不完整 | 修改 exporter 配置启用所需采集项,重启服务 |
| 复制相关指标为空或为 0 | 监控账号没有 REPLICATION CLIENT 权限,或实例本身不是从库 | 确认账号权限,排除非从库实例的误报 |
| 告警一直显示 PENDING | 条件持续时长未达到 for 参数要求 | 在规则文件中拉长或缩短 for 参数,重新加载规则 |
| 告警通知发送失败 | Alertmanager 路由配置错误、模板引用了不存在的标签、通知接口不可达 | 检查 Alertmanager 日志,验证模板变量,手动测试通知接口 |
| Grafana 面板无数据 | 数据源配置错误、PromQL 查询使用不对、面板变量为空 | 先在 Prometheus 里执行相同查询验证,数据源测试连通性,检查变量过滤 |
6.2 一些建议与经验心得
最后总结几条经验,不一定都写在文档里,但都是实际踩坑后攒下来的。
第一,版本兼容性要心里有数。Prometheus、exporter、Grafana 各自的版本更新节奏不同,我遇到过 exporter 老版本不支持新版本 MySQL 的某个状态变量,导致部分指标一直为空。更新 exporter 到官方最新稳定版,能省掉很多兼容性烦恼。
第二,抓取频率和存储保留期要提前规划。默认的 15 秒抓取间隔在大部分场景够用,但指标量很大、长期存储的话,时间序列数据膨胀得很快。我给不同环境的 Prometheus 设置了不同的保留期,测试环境保留两周,生产环境保留一个月,超过期限的数据自动清理,避免磁盘撑爆。
第三,mysqld_exporter 的配置不是越全越好。每个额外开启的采集器都意味着更多的 SQL 查询和更大的指标量。某次我一股脑把所有采集器全开,结果 exporter 本身成了数据库的压力来源,每次抓取要执行大量 SHOW 命令。按需开启,够用就好。
第四,告警阈值一定基于自己数据的分布来定。新搭建的监控系统前两周不要急着配告警,先让 Prometheus 收集数据,观察正常业务下的指标波动范围,再根据这个范围设置阈值。我见过不少团队直接把网上的阈值配上去,结果告警一天响几十次,最后所有人对告警彻底麻木。
第五,监控系统的可用性本身也要兜底。Prometheus 挂掉了,监控就成瞎子。给 Prometheus 服务端也配上进程级别的守护和端口存活检测,有条件的加上另一个监控节点做交叉检测。至少要做到 Prometheus 挂了,还能有人被通知到。
我个人在实际维护中的体会是,MySQL 监控体系的建设不是一劳永逸的活,随着业务增长,新的指标需求、新的告警策略、新的面板展示方式都会不断出现。把这套基础设施搭好,后续的调整和扩展就有了稳固的地基。希望这篇踩坑记录能让你少走一些弯路。