简介:面向运维与云计算从业者的Prometheus(普罗米修斯)监控MySQL详细操作文档,系统讲解完整监控链路,涵盖mysql_exporter部署、静态配置与服务发现、Alertmanager报警等核心环节。资源为单份docx文档,共1个文件,压缩包约6MB,内容以详细操作步骤与命令为主,适合需要落地开源监控方案的Linux运维、DBA及云平台工程师参考。文档从创建MySQL监控账号与授权讲起,逐步演示二进制安装mysql_exporter、编写.my.cnf连接配置、通过systemd托管服务,并给出Prometheus抓取指标与对接Alertmanager的配置要点,同时附带Docker容器环境下的监控示例,便于在真实业务场景中直接复用。目前已有1275人学习下载,适合希望快速掌握Prometheus监控MySQL实操细节的技术人员。
1. 监控MySQL前先想清楚:promethues到底监控的是什么
先说个容易翻车的现实:很多人拿到「promethues监控mysql」这个需求,第一反应是装个 mysqld_exporter、配一下 Prometheus、再导入一个 Grafana 模板就完事。结果跑了两天发现,图表有缺口、告警乱报、指标数值和业务实际感受对不上,最后这套监控就变成墙上的装饰屏。标题里 promethues 的正确拼写其实是 Prometheus,监控 MySQL 最难的环节根本不在搭链路,而在指标的语义辨析:你采回来的每个数字到底代表什么、它掉了意味着什么、它涨了又是什么在变。
这篇文章按我实际落地过的方式把整条链路拆开:从采集器选型、exporter 部署、Prometheus 抓取配置、告警规则,到最容易踩的五个坑,最后落在性能采集的进阶调优上。新手可以照着每一步操作走,熟手可以直接跳到第 5 章和第 6 章看边界。适合的人只有一种:你手上有一台或一批 MySQL,需要知道它是不是真的健康,而不是只想要一个看起来花花绿绿的监控大屏。
2. 采集原理与选型:为什么MySQL监控是这么个架构
2.1 Prometheus 的 pull 模型:MySQL 本身没有 /metrics 端点
先理解 Prometheus 监控第三方组件的通用套路。Prometheus 是 pull 模型,它定期去访问一个 HTTP 端点拉取指标文本,这个端点返回的是符合文本格式的 metric 数据。MySQL 作为一个数据库服务,自身没有这个 HTTP 端点,它对外暴露的是 SQL 接口,你可以连上去执行SHOW GLOBAL STATUS、查information_schema或performance_schema。
所以中间必须有一个人把 MySQL 的内部状态翻译成 Prometheus 能抓的格式。mysqld_exporter 干的就是这件事:它启动后开一个 HTTP 端口,每次被 Prometheus 抓取时,就连接 MySQL、执行一组预先定义好的查询、把结果转换成 metrics 文本返回。这个架构决定了几个边界:exporter 不主动往 Prometheus 推数据,抓取节奏由 Prometheus 控制;exporter 本身不存储数据,只做翻译;exporter 的状态只反映最后一次抓取是否成功,不能拿它当数据库探针用。
理解了这一点,后面所有配置就顺了。比如抓取频率调高了,压力是从 Prometheus 到 exporter 再到 MySQL 的查询链路,不是 Prometheus 单方面的事。再比如 exporter 挂了或网络不通,Prometheus 只会记录这个 target 为 down,业务实际完全正常的情况也经常出现。
2.2 mysqld_exporter 采集数据的四个来源
mysqld_exporter 的采集逻辑不是只跑一条 SQL,它按功能模块组织,常见的指标来源分四类。
第一类来自SHOW GLOBAL STATUS,这是连接数、线程数、慢查询数、吞吐量等核心状态指标的主要来源,比如Threads_connected、Queries、Slow_queries、Uptime、Bytes_received。这类指标最稳定,基本所有 MySQL 版本都返回同样的字段名。
第二类来自SHOW GLOBAL VARIABLES,这是配置类指标,比如max_connections、innodb_buffer_pool_size、wait_timeout、long_query_time。这类指标变化频率极低,但非常重要,因为告警阈值经常需要和它做对比,比如当前连接数与最大连接数的比值。
第三类来自information_schema,典型指标是Innodb_buffer_pool_pages_total、Innodb_buffer_pool_pages_free,以及部分表相关的元数据。
第四类来自performance_schema,这能拿到更细的等待事件、表 IO、索引使用情况。性能相关指标默认是部分开启的,完全采集需要打开对应的 collector 开关。这部分细节放到第 6 章展开。
需要特别注意:不同版本的 MySQL,某些状态变量的名字和行为有差异,比如 MySQL 8.0 里部分变量改名或废弃。所以在写告警规则之前,先花十分钟SHOW GLOBAL STATUS LIKE 'Threads%'核对一遍字段名,比在 Grafana 里反复查表达式靠谱得多。
2.3 选型结论:用官方 exporter,别自己写采集脚本
有些人觉得一个脚本定时执行mysql -e "show status"然后推给 Prometheus 也行,短期跑通确实可以,但长期维护有三个问题。
首先是指标格式,Prometheus 的文本格式虽然简单,但要处理标签、类型、时间戳,脚本很容易写错。其次是采集语义,exporter 每次抓取会记录抓取时刻的瞬时值,不是两次抓取之间的平均值,脚本如果用watch或sleep循环,时间误差会直接反映在数据质量上。第三是 exporter 自带一组现成的 collector 开关和指标命名体系,社区里所有仪表盘模板都是按这套命名来的,自己造一套指标名,意味着所有现成模板都用不了。
所以结论很直接:监控 MySQL 用官方维护的 mysqld_exporter,不要自己写。只有在极少数场景下才考虑替代方案,比如想要更细粒度的 SQL 级性能分析,那会用到更重的采集方案,超出了本文主题,暂不讨论。
3. 把mysqld_exporter部署起来:账号、配置和验证
3.1 创建监控账号:不要给 exporter 用 root
exporter 要连 MySQL 执行查询,就必须有账号。但给 root 是最省事也最危险的做法:exporter 配置文件里存放的是明文密码,exporter 进程只做抓取翻译,根本不需要超级权限,一旦这个账号泄漏,等于直接暴露数据库的全部权限。我一般会单独创建一个监控账号,权限按需给最小集。
CREATE USER 'mysqld_exporter'@'%' IDENTIFIED BY 'exporter_pass_2024'; GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'mysqld_exporter'@'%'; FLUSH PRIVILEGES;这段 SQL 做了三件事:创建专用账号、授权、刷新权限。PROCESS权限用于采集线程相关的状态,REPLICATION CLIENT用于获取主从复制位置和延迟信息,SELECT用于查询information_schema和performance_schema。如果你的 MySQL 启用了performance_schema,且想要更深的指标,还需要额外授权SHOW VIEW或对特定库的查询权限,这个按版本和需求加。注意 8.0 里PROCESS权限名字没有变,但密码插件默认是caching_sha2_password,如果 exporter 版本太老可能连不上。
这里的'%'表示允许任何主机连接,实际部署时建议限制为 exporter 所在机器的内网 IP,比如'mysqld_exporter'@'192.168.1.10'。权限最小化这件事,值得多花一分钟。
3.2 配置连接参数:环境变量和配置文件两种写法
mysqld_exporter 通过环境变量DATA_SOURCE_NAME获取数据库连接字符串,这个变量在 systemd 里配置比较麻烦,更常见的做法是把连接信息写进一个配置文件,然后在启动命令里通过--config.my-cnf指定。下面是我用的配置文件写法。
[client] user=mysqld_exporter password=exporter_pass_2024 host=127.0.0.1 port=3306这个格式和 MySQL 客户端配置完全一致,exporter 会直接把它当作 MySQL 客户端的连接参数来用。如果你是用 systemd 管理 exporter,可以在 unit 文件里指定配置文件路径。假设配置文件放在/etc/mysqld_exporter/.my.cnf,权限要改成 600,因为里面有明文密码。
有一种情况要单独处理:如果 MySQL 跑在远端,不建议把 exporter 也部署在数据库机器上,让每个 MySQL 实例对应一个 exporter。exporter 本身很轻,CPU 和内存占用都比较小,独立部署虽然多了一个进程,但故障隔离更清晰,Prometheus 该抓谁一目了然。
3.3 启动 exporter 并验证采集端点
配置文件弄好后,先用命令行前台方式启动一次,确认能正常拉起再注册成服务。
./mysqld_exporter --config.my-cnf=/etc/mysqld_exporter/.my.cnf --web.listen-address=:9104 --collector.global_status --collector.global_variables启动后紧接着用两条命令验证。第一条是看进程是否活着,第二条是直接抓取一次 metrics 文本,确认返回内容里有 MySQL 的指标。
curl http://127.0.0.1:9104/metrics | grep -E "mysql_up|mysql_global_status_threads_connected|mysql_global_variables_max_connections"如果看到mysql_up返回 1,说明 exporter 到 MySQL 的连接正常;如果返回 0,说明连接失败,需要去检查账号密码、网络和 MySQL 的 bind-address。mysql_global_status_threads_connected和mysql_global_variables_max_connections这两条是最基础的连接数与最大连接数指标,后面做告警要用。
为什么 grep 这两条而不是直接看一整页?因为 exporter 默认采集的指标非常多,全量输出可能上千行,先确认最核心的几条返回了,再逐步检查其他模块。如果某些指标没有出现在输出里,大概率是对应的 collector 没开,这个回到启动参数里检查。
3.4 注册成 systemd 服务:别用 nohup 顶着跑
验证没问题后,把 exporter 注册成 systemd 服务。直接 nohup 跑虽然也能用,但进程退出了没人拉起、开机不自启,生产环境不建议这么干。
[Unit] Description=Prometheus MySQL Exporter After=network.target After=mysqld.service [Service] Type=simple User=prometheus Group=prometheus ExecStart=/usr/local/bin/mysqld_exporter \ --config.my-cnf=/etc/mysqld_exporter/.my.cnf \ --web.listen-address=:9104 \ --collector.global_status \ --collector.global_variables Restart=always RestartSec=5 [Install] WantedBy=multi-user.target这个 unit 文件里有几个细节值得注意。Type=simple表示把进程直接跑在前台,systemd 认为主进程退出即服务结束。User和Group用了专用账号而不是 root,降低进程权限。Restart=always保证进程异常退出后 5 秒自动重启。写好后systemctl daemon-reload && systemctl enable --now mysqld_exporter就能开机自启并立即运行。
多实例场景下,每台 MySQL 对应一套上述配置,唯一需要改的是--web.listen-address的端口和配置文件里的 host/port。不要尝试用一个 exporter 进程连接多个 MySQL 实例,那是用复杂化换不明显收益,出了问题更难排查。
4. Prometheus侧配置:抓取、告警规则与可视化
4.1 在 prometheus.yml 里添加抓取 job
exporter 已经暴露了 9104 端口,接下来要让 Prometheus 去抓它。配置在prometheus.yml的scrape_configs段里新增一个 job。
scrape_configs: - job_name: 'mysql' static_configs: - targets: ['192.168.1.10:9104'] labels: instance: 'mysql-prod-01' scrape_interval: 15s scrape_timeout: 10s这里的job_name是这一组监控对象的逻辑名,建议按业务命名而不是按机器命名。targets填 exporter 的 IP 和端口,labels.instance单独做一层标签,因为如果直接暴露 IP,后面在 Grafana 里显示的就是一串数字,谁看都要猜一次这是什么环境。scrape_interval和scrape_timeout分别表示抓取频率和单次抓取超时时间,默认 15s 和 10s 对 MySQL 场景是合理的。
有一个参数改动要谨慎:scrape_timeout不能接近scrape_interval。如果设成 15s 抓一次、超时 12s,一次抓取慢一点,下一次抓取就接不上,图表上表现为锯齿和缺口,这不是监控目标的问题,是配置问题。如果 exporter 返回数据非常慢,优先排查 MySQL 端是否有大查询阻塞了 exporter 的查询,而不是一味调大超时时间。
4.2 告警规则:连接数、探活、主从延迟和慢查询
Prometheus 里告警规则写在独立规则文件中,在prometheus.yml里用rule_files引入。下面是一组最基础的 MySQL 告警规则。
groups: - name: mysql_alerts rules: - alert: MysqlInstanceDown expr: mysql_up == 0 for: 1m labels: severity: critical annotations: summary: "MySQL实例 {{ $labels.instance }} 不可达" - alert: MysqlHighConnectionCount expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections > 0.8 for: 5m labels: severity: warning annotations: summary: "MySQL实例 {{ $labels.instance }} 连接数超过80%" - alert: MysqlSlaveReplicationLag expr: mysql_slave_status_seconds_behind_master > 30 for: 2m labels: severity: warning annotations: summary: "MySQL主从延迟超过30秒"第一条mysql_up == 0是探活告警,需要注意它的语义:它表示 Prometheus 最后一次抓取 exporter 失败,不等于数据库死了,更不等于业务不可用,所以for: 1m给了一个持续时间过滤,避免 exporter 重启瞬间的误报。
第二条连接数告警用了除法表达式,把当前连接数和最大连接数做比值,这是比单纯告警threads_connected > 200更合理的做法,因为不同实例配置不同,硬编码阈值换个环境就要改。0.8 这个比例也值得分析一下:连接数在正常业务下有波动,瞬时超过 80% 不代表马上要挂,所以for: 5m过滤掉了毛刺;如果这个告警开始频繁出现,要去看的是应用连接池上限和max_connections的关系,而不是把阈值调到 0.95。
第三条主从延迟告警依赖mysql_slave_status_seconds_behind_master,但这里有一个前提:这个指标来自SHOW SLAVE STATUS,只有从库上才有,主库上这个指标永远为空。所以同一个告警规则最好通过标签区分实例角色,或者为主从实例分别配置不同规则文件。
4.3 Grafana 展示:导入模板后一定要校验数值
采集和告警都通了以后,Grafana 负责把指标变成可视化的图表。通常做法是去 Grafana 的官方仪表盘市场找一个 MySQL 模板,下载 JSON 文件后导入。这个方法没问题,但导入之后必须做一轮校验。
校验什么?第一,检查mysql_global_status_threads_connected这个图表的当前值和你在 MySQL 里执行SHOW GLOBAL STATUS LIKE 'Threads_connected'看到的值是否一致,不一致说明模板里可能用了错误的查询条件。第二,检查抓取时间范围,模板默认的时间跨度可能是最近 6 小时,但你的数据只存了 24 小时,区间外的图是空的,不属于故障但看起来像故障。第三,检查单位,连接数是个纯数字,但有些模板会把吞吐量指标配成错误的单位前缀,导致数值差了一千倍。
模板只是一个起点,数据源正确、指标语义正确才是监控可信的根基。Grafana 图的展示没问题,不代表告警规则没问题,两者分开验证,不要混在一起判断。
5. 避坑指南:promethues监控mysql最容易翻车的5个细节
5.1 现象:监控图表有缺口,采集断断续续
图表上出现周期性缺口,比如每 15 分钟缺一段,或者每天固定时间缺一块。一开始可能会怀疑数据库出问题了,但业务正常运行。
原因通常是两类的叠加:一类是抓取超时,Prometheus 默认scrape_timeout如果是 10s,但 exporter 在业务高峰期查询performance_schema耗时超过 10s,这次抓取就被放弃;另一类是采集端口的连接数被系统限制,高并发场景下 exporter 进程的可用文件描述符耗尽,新连接直接被拒。
解决方式是分层排查:先看 Prometheus target 页面里每个抓取点的 ERROR 信息,区分是 timeout 还是 connection refused。如果是 timeout,把该实例的scrape_timeout单独调大,同时精简 exporter 的 collector 开关,关掉不用的模块。如果是 connection refused,提高 exporter 进程的ulimit -n。不要盲目全局调大超时,那样只会让问题后移。
5.2 现象:exporter 把数据库连接数打高了
部署完监控后,MySQL 的Threads_connected比之前高了一截,有些业务本来连接数就接近上限,这下直接触发告警。
原因在于 exporter 每次被 Prometheus 抓取时都会创建新的数据库连接,如果同时有多个 Prometheus 实例在抓同一个 exporter,或者抓取间隔太短,连接数就成倍增加。另一个次要因素是监控账号用%做主机白名单,MySQL 每来一个新 IP 都会尝试建连。
解决方法是先数一数有几个 Prometheus 在抓这个 target,把多余的抓取配置删掉;然后把 exporter 的配置里加上--collector.auto_increment.columns=false这类不常用的 collector 直接关闭,减少每次抓取执行的查询数。更根本的做法是检查 exporter 的 DB 连接池参数,部分版本支持设置最大连接数,把它限制在 3 以内,毕竟一次抓取根本不需要太多连接。
5.3 现象:指标全是 0 或 NaN,图是平的
Grafana 图能出,但连接数、慢查询数全是 0,吞吐量是 NaN,看起来数据库像是完全空闲,但业务端明明在跑。
原因是权限不够或 collector 没开。SHOW GLOBAL STATUS需要PROCESS权限,某些信息结构表需要SELECT权限,如果 exporter 用的账号缺权限,查询会失败但 exporter 不会报错,只是对应指标返回空值或 0。另一类情况是 MySQL 8.0 里某些状态变量改名了,新版本 exporter 跟不上,指标改名导致表达式匹配不到。
排查时不要盯着 Grafana 看,直接 curl exporter 的/metrics端点,grep 对应的指标名,看返回值是空还是 0。如果返回空,说明采集模块有问题;如果返回 0,说明查询执行了但没数据,去 MySQL 里手动执行同一条 SQL 确认到底有没有数。
5.4 现象:Prometheus 本地数据把磁盘撑爆了
监控跑了两三个月,Prometheus 所在机器的磁盘使用率持续上涨,最后把 Prometheus 自身都压死了。
原因是默认数据保留策略太长。Prometheus 的--storage.tsdb.retention.time如果没设,默认按 15 天保留,听起来不长,但如果抓取的实例多、指标基数大,每个序列每个采集周期都写一个数据点,15 天数据量是很大的。MySQL 的 exporter 全量采集时指标数轻松上千,在多实例场景下序列数翻倍。
解决方式是按需调短保留时间,或者在启动参数里同时限制时间保留和大小。比如设置--storage.tsdb.retention.time=7d --storage.tsdb.retention.size=20GB,两个参数同时生效,达到任何一个就会清理旧数据,这两个参数都不需要重启 Prometheus,改启动命令重启即可生效。另一个手段是控制指标基数,在prometheus.yml的metric_relabel_configs里 drop 掉明确不用的指标。
5.5 现象:告警发了一堆,但数据库其实是健康的
告警风暴,一晚上收到几十条 MySQL 告警,但排查发现数据库负载很低、业务完全正常。
这种大多是告警规则没加for持续时间,或者表达式里用了瞬时极易波动的指标。比如mysql_global_status_threads_connected在业务瞬时高峰超过阈值一次,就会触发告警,但 10 秒后就回落了。另一个典型是把mysql_up == 0当成数据库宕机告警,实际上 exporter 或网络抖动也会触发它。
解决方式是给每条告警谨慎设置for的值。探活类可以短一点,比如 1 分钟;连接数、延迟类至少 5 分钟起步。同时区分告警等级:mysql_up == 0标记为 critical 但说明文案里写明是采集链路异常,连接数超过 80% 是 warning,真正到 95% 以上再升 critical。告警的目的是让人看一眼就知道该怎么处理,不是让人大半夜尝试分辨哪种情况不用起床。
6. 进阶:用 collector 开关和性能库把监控做到能定位问题
到这一步,基础的采集、告警、展示已经齐了,再往前一步是让监控能回答「为什么」的问题。mysqld_exporter 默认采集的指标以状态变量为主,这些数据告诉你系统当前是什么状态,但很难告诉你哪个环节在耗时、哪张表在竞争。真正要定位问题,需要打开 performance_schema 相关的 collector。
我常用的启动参数加上这几个:
--collector.perf_schema.eventsstatements \ --collector.perf_schema.table_io_waits \ --collector.perf_schema.index_io_waits \ --collector.info_schema.innodb_metrics \ --collector.slave_status其中perf_schema.table_io_waits和index_io_waits会让 exporter 读取performance_schema.table_io_waits_summary_by_table这类汇总表,返回每个表的读写等待时间。在 Grafana 里做一张表,按wait_time排序,业务卡顿的时候直接能看出来是不是某一张表的行锁竞争在飙升。eventsstatements则是从事件维度做聚合,能够按 SQL 类型看总耗时占比,适合定位到底是不是某种查询拖慢了整体。
开启这些 collector 前先确认两件事:一是 MySQL 的performance_schema没有关闭,默认是开的,但有些运维会把performance_schema=OFF写在配置文件里省内存,开了 collector 也采不到数据;二是确认监控账号对这些表的 SELECT 权限。这类指标的基数比状态变量大得多,一台活跃业务库的table_io_waits可能就是几百条序列,Prometheus 的存储压力会上升,生产环境建议单独评估后选择开启的 collector 数量。
验证这些 collector 是否生效,还是回到 curl 端点抓指标文本,搜索perf_schema和table_io_waits关键字,出现对应的 metric 说明采集链路已通。
另一个我实际用得比较多的技巧是连接状态细分。默认的mysql_global_status_threads_connected只告诉你当前有多少连接,没法区分是活跃连接还是睡眠连接。exporter 有单独的状态指标,抓一下threads_running和threads_connected做对比:如果 connected 很高但 running 很低,说明连接池开太大、大多数连接在闲置;如果 running 持续接近 connected,说明业务并发真的高。这两个数字的比例,比单独看任何一个都有用。加上max_connections做归一化,你可以在一个面板里直接判断当前实例的负载余量。
我自己踩过最深的一个坑是:光有告警没有归因路径,凌晨三点收到连接数告警,起来看一眼 Grafana 图,能确认连接数确实高,但查不出是哪来的连接。后来把连接来源相关的指标和processlist快照机制补上,再遇到同类告警,第一件事就是看是来自 App 服务器还是监控自身。现在我养成的习惯是:任何指标进监控之前,先问自己一句「如果这个数字异常,下一步该看哪张图」,能回答就留下,不能回答就先别加。监控不是指标越多越好,够用、能顺着查下去才是标准。希望这些经验能帮你在搭 promethues 监控 mysql 这条路上一开始就走对方向。
本文还有配套的精品资源,点击获取