Prometheus监控MySQL实战:从exporter部署到告警与性能调优 简介面向运维与云计算从业者的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 用 rootexporter 要连 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_exporter192.168.1.10。权限最小化这件事值得多花一分钟。3.2 配置连接参数环境变量和配置文件两种写法mysqld_exporter 通过环境变量DATA_SOURCE_NAME获取数据库连接字符串这个变量在 systemd 里配置比较麻烦更常见的做法是把连接信息写进一个配置文件然后在启动命令里通过--config.my-cnf指定。下面是我用的配置文件写法。[client] usermysqld_exporter passwordexporter_pass_2024 host127.0.0.1 port3306这个格式和 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] DescriptionPrometheus MySQL Exporter Afternetwork.target Aftermysqld.service [Service] Typesimple Userprometheus Groupprometheus ExecStart/usr/local/bin/mysqld_exporter \ --config.my-cnf/etc/mysqld_exporter/.my.cnf \ --web.listen-address:9104 \ --collector.global_status \ --collector.global_variables Restartalways RestartSec5 [Install] WantedBymulti-user.target这个 unit 文件里有几个细节值得注意。Typesimple表示把进程直接跑在前台systemd 认为主进程退出即服务结束。User和Group用了专用账号而不是 root降低进程权限。Restartalways保证进程异常退出后 5 秒自动重启。写好后systemctl daemon-reload systemctl enable --now mysqld_exporter就能开机自启并立即运行。多实例场景下每台 MySQL 对应一套上述配置唯一需要改的是--web.listen-address的端口和配置文件里的 host/port。不要尝试用一个 exporter 进程连接多个 MySQL 实例那是用复杂化换不明显收益出了问题更难排查。4. Prometheus侧配置抓取、告警规则与可视化4.1 在 prometheus.yml 里添加抓取 jobexporter 已经暴露了 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.columnsfalse这类不常用的 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.time7d --storage.tsdb.retention.size20GB两个参数同时生效达到任何一个就会清理旧数据这两个参数都不需要重启 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_schemaOFF写在配置文件里省内存开了 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 这条路上一开始就走对方向。本文还有配套的精品资源点击获取