
Prometheus已经能看服务器CPU、内存,不代表它天然就知道MySQL现在有多少连接、慢查询有没有突增、主从复制是不是异常。数据库内部这些状态,需要有人先把它们转换成Prometheus能抓取的指标,这正是mysqld_exporter在整条链路里的位置。
这次我在CentOS 7上把这条链从头走了一遍:先下载并启动mysqld_exporter,让它通过9105暴露指标;再把Prometheus指向这个端点,确认Target能够正常抓取;随后加入MySQL宕机、复制异常、连接数过高、慢查询等告警规则,并实际验证MySQL停止后告警出现、恢复后状态回归。
后半部分再处理一个更现实的问题:如果Prometheus和MySQL不在同一局域网,怎样让远端Prometheus继续抓到9105指标。这里会先验证随机公网地址,再配置固定二级子域名。
不过这篇配置里有几处不能机械照抄:授权用户写的是mysqld_exporter,客户端配置却写成exporter;systemd路径和手动启动路径也分别用了mysqld_exporter与mysql_exporter。这些代码我会原样保留,但把风险位置明确指出。
对我来说,这类监控最重要的不是“多一个页面”,而是出了问题以后能不能马上知道问题在哪一层:是MySQL本身没启动、连接数接近上限、复制延迟,还是exporter根本没有采集成功。把这几个环节拆开验证,后面的排障会比只看日志清楚很多。

这篇最重要的不是记命令,而是先看懂四层关系:
MySQL → mysqld_exporter → Prometheus → 告警规则
MySQL 本身提供运行状态。
mysqld_exporter 负责读取这些状态,并转换成 Prometheus 能抓取的指标。
Prometheus 再定期抓取 9105,把指标保存下来并用于查询、规则评估和告警判断。
所以 mysqld_exporter 更像一座桥。
没有这座桥,Prometheus 并不会凭空知道 MySQL 内部的连接数、复制状态和慢查询情况。
在 Prometheus 相关下载页面获取 Linux 版本:

下载后上传到 /app:

也可以使用命令:
wget
https://github.com/prometheus/mysqld_exporter/releases/download/v0.18.0/mysqld_exporter-0.18.0.linux-amd64.tar.gz这里有一个细节值得注意。
代码块把:
wget
和下载 URL 分成了两行。
如果终端直接逐行执行,第一行只会执行一个不带参数的 wget。
真正执行时应该先确认命令格式,而不是机械复制。
执行:
tar -zxvf mysqld_exporter-0.18.0.linux-amd64.tar.gz
然后改名:
mv mysqld_exporter-0.18.0.linux-amd64/ mysqld_exporter
最终目录是:
/app/mysqld_exporter
后面的 systemd 配置也会用到这个路径。
在 MySQL 中执行:
GRANT REPLICATION CLIENT, PROCESS ON *.* TO 'mysqld_exporter'@'localhost' identified by '12345678';
GRANT SELECT ON performance_schema.* TO 'mysqld_exporter'@'localhost';
flush privileges;
这里创建的是:
mysqld_exporter@localhost
并授予:
REPLICATION CLIENTPROCESSperformance_schema.* 的 SELECT这比直接用 root 账号给 exporter 连接数据库更合理。
监控账号只拿采集指标需要的权限,长期维护也更容易控制。
接下来配置文件写的是:
[client]
user=exporter
password=12345678
这里用户名变成了:
exporter
而前面真正授权的是:
mysqld_exporter
这两者并不一致。
如果数据库里没有额外创建 exporter 用户,那么 exporter 启动以后很可能因为认证失败而无法采集。
所以真正执行时,必须让:
MySQL创建的用户名
和:
客户端配置中的 user
保持一致。
代码块按照已有操作原样保留,不在这里偷偷替换。
服务配置:
[Unit]
Description=exporter
After=network.target
[Service]
Type=simple
User=prometheus
ExecStart=/app/mysqld_exporter/mysqld_exporter --config.my-cnf="/app/mysqld_exporter/localhost_db.cnf" --web.listen-address=":9105"
Restart=on-failure
[Install]
WantedBy=multi-user.target
这里有三个关键点:
prometheus/app/mysqld_exporter/localhost_db.cnf:9105因此如果后面服务启动失败,优先检查:
prometheus 用户;/app/mysqld_exporter/mysqld_exporter 是否存在;prometheus 用户是否有读取权限。手动方式是:
sudo -u prometheus /app/mysql_exporter/mysqld_exporter --config.my-cnf="/app/mysql_exporter/localhost_db.cnf" --web.listen-address=":9105"这里使用的是:
/app/mysql_exporter/
而前面实际改名后的目录是:
/app/mysqld_exporter/
也就是说:
mysqld_exporter
和:
mysql_exporter
在路径里混用了。
这不是单纯的写法差异,而会直接影响命令能不能找到文件。
如果 systemd 方式能启动,但手动方式报“文件不存在”,应该先核对这个路径。
浏览器通过服务器 IP 加 9105 访问:

这一步能证明的是:
mysqld_exporter进程已经监听9105,并且Web端点可访问。
如果这一步失败,就不要急着去改 Prometheus。
先把 exporter 自身跑通。
编辑配置文件:
vi prometheus.yml加入目标:
- targets: ["localhost:9105"]
labels:
app: "mysql_exporter"
然后重启 Prometheus:
systemctl restart prometheus打开 Prometheus 9090:

Target 被检测到:

做到这里,才算真正打通:
MySQL → mysqld_exporter:9105 → Prometheus
示例里只展示了:
targets
和:
labels
但没有把完整的 scrape_configs、job_name 一起展示。
所以如果直接把这两行粘贴到 prometheus.yml 任意位置,配置未必有效。
真正执行时,应该确认它位于正确的 scrape job 下面。
这篇保留现有片段,但不把它描述成可以脱离上下文直接复制的完整配置。
示例规则如下:
groups:
- name: mysql-alerts
rules:
# 1. MySQL 实例宕机或 exporter 无法采集
- alert: MySQLDown
expr: mysql_up == 0
for: 1m
labels:
severity: critical
annotations:
summary: "MySQL instance down"
description: "MySQL instance {{ $labels.instance }} has been down for more than 1 minute."
# 2. 主从复制中断(仅适用于有从库的场景)
- alert: MySQLReplicationBroken
expr: mysql_slave_status_seconds_behind_master > 300
for: 2m
labels:
severity: warning
annotations:
summary: "MySQL replication lag"
description: "MySQL replication on {{ $labels.instance }} is lagging by {{ $value }} seconds."
# 3. 主从 IO/SQL 线程停止
- alert: MySQLReplicationIOThreadDown
expr: mysql_slave_status_master_server_id > 0 and mysql_slave_status_slave_io_running == 0
for: 1m
labels:
severity: critical
annotations:
summary: "MySQL replication IO thread stopped"
description: "Slave IO thread is not running on {{ $labels.instance }}."
- alert: MySQLReplicationSQLThreadDown
expr: mysql_slave_status_master_server_id > 0 and mysql_slave_status_slave_sql_running == 0
for: 1m
labels:
severity: critical
annotations:
summary: "MySQL replication SQL thread stopped"
description: "Slave SQL thread is not running on {{ $labels.instance }}."
# 4. 连接数使用率过高(超过 80%)
- alert: MySQLTooManyConnections
expr: (mysql_global_status_threads_connected / mysql_global_variables_max_connections) * 100 > 80
for: 2m
labels:
severity: warning
annotations:
summary: "High MySQL connection usage"
description: "{{ $labels.instance }}: MySQL connection usage is {{ $value | printf \"%.2f\" }}% of max_connections."
# 5. 慢查询数量突增(过去5分钟慢查询 > 10 条)
- alert: MySQLHighSlowQueries
expr: rate(mysql_global_status_slow_queries[5m]) > 0.1 # ≈ 每分钟 > 6 条
for: 5m
labels:
severity: warning
annotations:
summary: "High rate of slow queries on MySQL"
description: "{{ $labels.instance }}: Slow query rate is {{ $value | printf \"%.2f\" }} per second."
这组规则覆盖了几类常见数据库状态:
这些规则的意义是:
Prometheus已经不只是“采集”,开始对指标做条件判断。
进入配置文件:
vi prometheus.yml把规则文件加入:
rule_files:
- "/app/prometheus/1.yml"
- "/app/prometheus/2.yml"
- "/app/prometheus/3.yml"
- "/app/prometheus/4.yml"
然后重启:
systemctl restart prometheus再次打开 Prometheus:

可以看到告警规则:

此时 MySQL 没有启动:

所以告警进入异常状态。
随后重新启动 MySQL:

状态恢复。
这段验证非常重要。
因为它不只是“规则写进去了”,而是实际证明:
MySQL状态变化 → exporter指标变化 → Prometheus规则状态变化
这条链已经工作。
章节标题写的是:
“使用alertmanager配置mysql_exporter告警”。
但正文这一段实际展示的是:
没有继续展示:
所以更准确的说法应该是:
Prometheus告警规则已经配置并触发。
如果前面已经另外部署并接入 Alertmanager,它可以继续接收这些告警。
但仅凭当前内容,还不能证明通知渠道已经完整跑通。
这组规则里包含:
mysql_slave_status_seconds_behind_mastermysql_slave_status_slave_io_runningmysql_slave_status_slave_sql_running如果当前 MySQL 没有主从架构,这些规则没有实际意义。
所以不要为了“规则越多越完整”全部照搬。
更合理的是:
按自己的数据库架构选择规则。
单机 MySQL 可以先保留:
复制类规则只在真的存在从库时使用。
到这里,如果 MySQL、mysqld_exporter 和 Prometheus 都在同一个网络中,本地监控已经完成。
但如果:
Prometheus 就需要一个能够访问到 exporter 的网络入口。
这才是后面公网部分的用途。
执行:
sudo curl https://get.cpolar.sh | sh
安装后查看服务:
sudo systemctl status cpolar
然后通过:
服务器IP:9200
进入 Web UI:

创建隧道:
mysql_exporterhttp9105China Top
创建以后查看在线隧道:

从外部访问:

页面能打开以后说明:
外部浏览器 → 公网地址 → cpolar → 9105 → mysqld_exporter
这条网络链路已经通了。
Prometheus 示例配置:
- targets: ["38c53143.r2.cpolar.top"]
labels:
app: "mysql_exporter"
随后显示抓取成功:

这一段证明的是:
异地 Prometheus 可以通过公网地址访问 exporter 指标端点。
但仍然需要注意:
示例 target 只写了主机名,没有展示完整 job、scheme 和端口上下文。
所以它仍然更像局部配置片段,而不是完整 scrape job。
mysqld_exporter 的指标页不是普通宣传页面。
它可能暴露:
这些信息本身就属于运维数据。
所以真正长期远程抓取时,不应该只考虑“Prometheus能不能访问”,还要考虑:
cpolar 只负责网络入口。
访问控制仍然需要结合实际环境设计。
如果只是临时测试远程抓取,随机地址已经足够。
先确认:
验证完成以后,再考虑长期固定地址。
进入预留页面:

示例名称使用:
mysqll

这里比 mysql 多了一个 l。
二级子域名本身可以自定义,所以技术上不一定有问题。
但如果希望名称和服务保持一致,自己配置时最好统一命名。
进入隧道列表:

修改:
China Top。更新:

查看在线隧道:

最后使用固定地址访问:

页面正常打开以后,固定入口就配置完成。
具体固定域名、套餐和使用规则可能变化,应以实际账号页面为准。
实际完成:
没有完整展示:
所以文章最准确的定位是:
把MySQL指标从数据库内部带到Prometheus,并继续验证告警规则和异地抓取。
这次真正跑通的是一条完整的MySQL指标链:MySQL负责提供运行状态,mysqld_exporter把这些状态暴露到9105,Prometheus再负责抓取、查询和评估告警规则。
最有价值的验证不是“页面能打开”,而是MySQL停止以后Prometheus告警真的出现,重新启动后状态又恢复。这说明从数据库状态变化到告警规则判断的链路确实在工作。
远程部分则把9105提供给异地Prometheus抓取,但指标端点本身属于运维信息,不应该因为能通过公网访问就忽略认证和暴露范围。
另外,部署里几个用户名和目录名不一致的问题一定要先核对清楚。把这些细节处理好以后,这套方案才更适合作为MySQL监控链的基础。