简介:这份文档面向运维工程师与云计算从业者,围绕 Prometheus 监控 MySQL 这一典型场景展开,适合已具备 Linux 与容器基础、希望把监控链路落到实处的读者参考。资源为 docx 单文档,文件总数 1 个,压缩包约 6MB,内容以图文与命令行示例混合编排,便于边看边在实验环境中复现。文档从 MySQL 侧授权专用账号讲起,覆盖 mysql_exporter 的二进制安装、.my.cnf 密码文件编写、systemd 单元文件托管与 9104 端口指标端点验证,并延伸到 Prometheus 静态配置、基于文件与 Consul 的服务发现、docker 容器服务发现,以及 Alertmanager 报警规则与通知渠道配置,同时给出采集 innodb、perf_schema、binlog 等指标的启动参数写法与排错思路。目前已有 1274 人学习下载,适合作为搭建 MySQL 监控体系的动手参考手册。
1. 为什么 MySQL 监控要从指标口径讲起:promethues 与 mysqld_exporter 的分工
搜 promethues 监控 MySQL 的人,通常已经有一台在跑的数据库,想尽快看到连接数、QPS、慢查询和 InnoDB 命中率,而不是先读一本 MySQL 架构书。Prometheus 负责拉取和存储时间序列,mysqld_exporter 负责把 MySQL 的SHOW GLOBAL STATUS、SHOW GLOBAL VARIABLES、PROCESSLIST、复制状态翻译成/metrics文本,Grafana 再把这些指标画成面板。这个拆分决定了后面所有配置:MySQL 侧只开最小权限,exporter 侧只暴露 HTTP 指标,Prometheus 侧只做抓取、标签和告警。适合已经会写 docker 命令、见过prometheus.yml,但还没把 MySQL 监控链路一次跑通的人。
2. 用 docker 安装 MySQL 8.0 并跑通 mysqld_exporter
2.1 docker 安装 MySQL 8.0 的最小命令与目录规划
2.1.1 启动一个可被监控的 MySQL 容器
常见 mysql 安装配置教程会先装 MySQL,这里直接用 docker 起一个最小实例,重点是后面 exporter 能连上。挂命名卷是为了容器重建后数据不丢,端口映射是为了宿主机能连进去建账号,字符集参数是为了避免中文乱码。
docker run -d \ --name mysql-monitor-demo \ -e MYSQL_ROOT_PASSWORD='Root@123456' \ -e MYSQL_DATABASE='appdb' \ -p 3306:3306 \ -v mysql_monitor_data:/var/lib/mysql \ mysql:8.0 \ --character-set-server=utf8mb4 \ --collation-server=utf8mb4_unicode_ci这段命令里,-d让容器后台运行,--name指定容器名,后面 exporter 可以用容器名访问;-e MYSQL_ROOT_PASSWORD只在数据目录为空时初始化 root 密码;-p 3306:3306把容器 MySQL 暴露到宿主机;-v mysql_monitor_data:/var/lib/mysql使用命名卷;mysql:8.0是热词里常见的安装版本;最后两行是 mysqld 服务端参数。如果宿主机已经有 MySQL,把左边端口改成3307。初始化需要几十秒,用docker logs -f mysql-monitor-demo看到ready for connections再继续。
2.1.2 建监控账号:为什么 exporter 不该用 root
用 root 进容器建一个专用账号:
CREATE USER 'exporter'@'%' IDENTIFIED BY 'Exporter@123' WITH MAX_USER_CONNECTIONS 3; GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'%'; GRANT SELECT ON performance_schema.* TO 'exporter'@'%'; FLUSH PRIVILEGES;PROCESS让 exporter 读取PROCESSLIST和部分 InnoDB 状态,REPLICATION CLIENT让 exporter 读取主从复制状态,SELECT ON *.*用于少量元数据查询,performance_schema的 SELECT 用于额外指标。MAX_USER_CONNECTIONS 3限制这个账号最多占 3 个连接,避免 exporter 异常时把连接数打满。不要给ALL或SUPER,监控账号只需要读状态,权限给多了是审计风险。
2.2 部署 mysqld_exporter 并指定 DATA_SOURCE_NAME
先创建自定义 docker 网络,让 exporter 和 MySQL 容器能用容器名互通:
docker network create mysql-monitor-net docker network connect mysql-monitor-net mysql-monitor-demo docker run -d \ --name mysqld-exporter \ --network mysql-monitor-net \ -p 9104:9104 \ -e DATA_SOURCE_NAME='exporter:Exporter@123@(mysql-monitor-demo:3306)/' \ prom/mysqld-exporterDATA_SOURCE_NAME的格式是user:password@(host:port)/,其中 host 填 MySQL 容器名,前提是两个容器在同一个自定义网络。如果密码里包含@、:、/等字符,要先做 URL 编码,否则 exporter 解析连接串会失败。-p 9104:9104把 exporter 的指标端口暴露到宿主机,Prometheus 容器如果也在同一网络,可以直接抓mysqld-exporter:9104。生产环境里常见做法是把 exporter 和 MySQL 部署在同一台机器,用127.0.0.1连接,减少网络暴露面。
2.3 验证 9104 端口的指标是否可用
先看几个关键指标是否出现:
curl -s http://127.0.0.1:9104/metrics \ | grep -E '^mysql_up|^mysql_global_status_threads_connected|^mysql_global_status_questions' \ | head -n 20mysql_up 1表示 exporter 能连上 MySQL;mysql_global_status_threads_connected是当前连接数;mysql_global_status_questions是自启动以来执行的语句总数。如果mysql_up 0,先看docker logs mysqld-exporter,通常是账号权限不足、host 写错或网络不通。下面这些指标是后面告警和面板的基础:
| 指标 | 含义 | 常用标签 |
|---|---|---|
mysql_up | exporter 与 MySQL 的连通性 | instance、job |
mysql_global_status_threads_connected | 当前连接数 | instance |
mysql_global_status_questions | 已执行语句总数 | instance |
mysql_global_status_innodb_buffer_pool_reads | InnoDB 物理读次数 | instance |
mysql_global_status_innodb_buffer_pool_read_requests | InnoDB 逻辑读次数 | instance |
mysql_global_status_slow_queries | 慢查询累计数 | instance |
mysql_global_variables_max_connections | 最大连接数 | instance |
mysql_global_status_uptime | MySQL 运行秒数 | instance |
这些指标名来自 exporter 对 MySQL 状态变量的映射,不同 MySQL 版本可能多出或少掉个别指标,配置告警前先用/metrics确认实际存在的名字。
3. Prometheus 抓取 MySQL 指标的 prometheus.yml 与 relabel 配置
3.1 prometheus.yml 里 mysql job 的最小写法
Prometheus 配置的核心是scrape_configs,每个 job 对应一组目标。MySQL 监控的 target 是 exporter 地址,不是 MySQL 地址,这一点最容易写错。
global: scrape_interval: 15s evaluation_interval: 15s scrape_configs: - job_name: 'mysql' metrics_path: /metrics static_configs: - targets: - 'mysqld-exporter:9104' labels: env: 'prod' role: 'primary' app: 'order'scrape_interval: 15s对大多数 MySQL 实例够用,再密会增加 exporter 查询和 Prometheus 存储压力;evaluation_interval控制告警规则评估频率。static_configs适合少量实例,实例多了应改用file_sd_configs或服务发现。targets写 exporter 的host:port,labels会给该目标下所有序列附加env、role、app标签,后面 Grafana 变量和 Alertmanager 路由都依赖这些标签。
3.2 用 relabel 给 MySQL 实例打环境、角色和业务标签
如果不想在 targets 里写死标签,可以用relabel_configs根据地址动态打标签。下面把instance从host:port改成纯 host,并按 IP 区分主从:
scrape_configs: - job_name: 'mysql' static_configs: - targets: - '10.0.0.11:9104' - '10.0.0.12:9104' relabel_configs: - source_labels: [__address__] regex: '([^:]+):(\d+)' target_label: instance replacement: '${1}' - source_labels: [__address__] regex: '10\.0\.0\.11:9104' target_label: role replacement: 'primary' - source_labels: [__address__] regex: '10\.0\.0\.12:9104' target_label: role replacement: 'replica' - target_label: env replacement: 'prod'relabel_configs在抓取发生前执行。第一条规则从__address__里提取 host 写入instance,这样 Grafana 下拉框里显示的是 IP,而不是带端口的地址。第二条和第三条按完整地址匹配,分别写入role=primary和role=replica。最后一条没有source_labels,直接给所有目标加env=prod。注意 Prometheus 的 relabel regex 默认锚定整个字符串,不需要自己写^和$。
3.3 目标抓取失败时按顺序查这 5 处
抓取失败不要一上来改配置,按下面顺序查能最快定位:
| 顺序 | 检查点 | 命令或位置 | 典型现象 |
|---|---|---|---|
| 1 | Prometheus Targets 页面 | http://prometheus:9090/targets | 状态 DOWN,错误含 connection refused |
| 2 | exporter 指标端点 | curl -v http://exporter:9104/metrics | 连接拒绝或 404 |
| 3 | exporter 日志 | docker logs mysqld-exporter | Access denied或dial tcp timeout |
| 4 | MySQL 账号权限 | SHOW GRANTS FOR 'exporter'@'%'; | 缺PROCESS导致mysql_up 0 |
| 5 | 网络与标签 | docker network inspect mysql-monitor-net | 容器名解析失败或标签覆盖 |
第 1 步先确认 Prometheus 自己怎么看目标,第 2 步绕过 Prometheus 直接验证 exporter,第 3 步看 exporter 连 MySQL 的报错,第 4 步确认权限和账号 host,第 5 步检查容器网络和 relabel 是否把关键标签覆盖掉。多数mysql_up 0都出在第 3 步和第 4 步之间。
4. MySQL 告警规则与 PromQL:连接数、QPS、慢查询、InnoDB 命中率
4.1 先把 8 个核心指标和它们的 PromQL 口径对齐
告警不准,通常是 Counter 和 Gauge 用错。下面这些口径可以直接抄:
| 指标 | PromQL 表达式 | 说明 |
|---|---|---|
| 连接数使用率 | mysql_global_status_threads_connected / mysql_global_variables_max_connections | Gauge 相除,大于 0.8 预警 |
| QPS | rate(mysql_global_status_questions[5m]) | Counter,必须加rate |
| 慢查询速率 | rate(mysql_global_status_slow_queries[5m]) | 大于 0.1 表示每 10 秒一条慢查询 |
| InnoDB 命中率 | 1 - rate(mysql_global_status_innodb_buffer_pool_reads[5m]) / rate(mysql_global_status_innodb_buffer_pool_read_requests[5m]) | 低于 0.95 关注 |
| 主从延迟 | mysql_slave_status_seconds_behind_master | 新版本指标名可能不同,先查/metrics |
| 运行时长 | mysql_global_status_uptime | 突降表示 MySQL 重启 |
| 连接失败速率 | rate(mysql_global_status_aborted_connects[5m]) | 阈值按业务定 |
| 表锁等待速率 | rate(mysql_global_status_table_locks_waited[5m]) | 元数据锁也会影响 |
threads_connected和max_connections是 Gauge,直接相除;questions、slow_queries、innodb_buffer_pool_reads是 Counter,必须用rate或increase。如果对 Counter 直接取值,得到的是一条一直上涨的曲线,告警会一直触发或永远不触发。
4.2 告警规则文件 mysql.rules.yml 的完整片段
把规则单独放一个文件,由prometheus.yml的rule_files加载:
groups: - name: mysql.rules rules: - alert: MySQLDown expr: mysql_up == 0 for: 1m labels: severity: critical annotations: summary: "MySQL 实例不可用 {{ $labels.instance }}" description: "mysqld_exporter 无法连接 MySQL,持续 1 分钟。" - alert: MySQLConnectionHigh expr: mysql_global_status_threads_connected / mysql_global_variables_max_connections > 0.8 for: 5m labels: severity: warning annotations: summary: "MySQL 连接数超过 80% {{ $labels.instance }}" - alert: MySQLSlowQueryBurst expr: rate(mysql_global_status_slow_queries[5m]) > 0.1 for: 10m labels: severity: warning annotations: summary: "MySQL 慢查询速率偏高 {{ $labels.instance }}" - alert: MySQLInnoDBHitLow expr: 1 - rate(mysql_global_status_innodb_buffer_pool_reads[5m]) / rate(mysql_global_status_innodb_buffer_pool_read_requests[5m]) < 0.95 for: 15m labels: severity: warning annotations: summary: "InnoDB 命中率低于 95% {{ $labels.instance }}"for给告警增加持续时间,避免瞬时抖动;labels.severity用于路由;annotations里的{{ $labels.instance }}会被替换成实例标签。加载新规则后,用promtool check rules mysql.rules.yml检查语法,再给 Prometheus 发 reload 请求或重启容器。
4.3 连接数、QPS、慢查询、InnoDB 命中率的参数阈值怎么定
阈值不要照搬 80%,按业务连接模型调。下面几条查询适合直接放进 Grafana 或告警:
# 连接数使用率,保留两位小数 round(100 * mysql_global_status_threads_connected / mysql_global_variables_max_connections, 0.01) # QPS,按实例聚合 sum by (instance) (rate(mysql_global_status_questions[5m])) # 慢查询速率 sum by (instance) (rate(mysql_global_status_slow_queries[5m])) # InnoDB 命中率 1 - sum by (instance) (rate(mysql_global_status_innodb_buffer_pool_reads[5m])) / sum by (instance) (rate(mysql_global_status_innodb_buffer_pool_read_requests[5m]))短连接为主的业务,连接数使用率 80% 可能正常;长连接池业务,50% 就要查连接池和回收策略。QPS 看趋势,不看单点,突然掉零通常比突然升高更危险。慢查询速率大于 0.1 表示每 10 秒一条慢查询,是否告警取决于业务对延迟的容忍度。InnoDB 命中率低于 95% 时,先查innodb_buffer_pool_size,再看有没有全表扫描。如果read_requests为 0,除法会得到 NaN,表达式里要加and ... > 0过滤。
4.4 Alertmanager 路由与告警抑制的常见做法
告警规则触发后交给 Alertmanager 分组和路由,常见配置如下:
route: receiver: 'default' group_by: ['alertname', 'env', 'instance'] group_wait: 30s group_interval: 5m repeat_interval: 4h routes: - matchers: ['severity="critical"'] receiver: 'oncall' continue: true - matchers: ['severity="warning"'] receiver: 'ops-mail' inhibit_rules: - source_matchers: ['alertname="MySQLDown"'] target_matchers: ['severity=~"warning|critical"'] equal: ['instance']group_by把同一实例、同一环境的告警聚在一起;group_wait给同一组告警留 30 秒聚合窗口;repeat_interval控制重复通知频率。inhibit_rules表示当MySQLDown已经触发时,抑制同一instance的其他告警,避免数据库已经不可用还收到一堆连接数、慢查询告警。新版 Alertmanager 使用matchers,老版本用match,升级时要注意。
5. Grafana 面板、指标对账与长期存储的进阶技巧
5.1 导入 MySQL 面板后必须改的三个变量
导入常见 MySQL 面板后,先改三个变量:instance、job、env。instance的 Query 一般写成label_values(mysql_up, instance),如果你在 relabel 里把instance改成了纯 host,这里要同步;job用label_values(mysql_up, job)并默认选mysql;env用label_values(mysql_up, env),默认选生产环境。数据源要选对 Prometheus,时间范围最好用面板自带的$__rate_interval,不要写死[5m],否则大时间范围下曲线会剧烈锯齿。
5.2 用 PromQL 和 MySQL 原生命令做一次指标对账
面板跑通后,用原生命令对一次账,确认 exporter 连的是正确实例:
SHOW GLOBAL STATUS LIKE 'Questions'; SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL STATUS LIKE 'Slow_queries';对应的 PromQL:
mysql_global_status_questions{instance="10.0.0.11"} mysql_global_status_threads_connected{instance="10.0.0.11"} rate(mysql_global_status_slow_queries{instance="10.0.0.11"}[5m])Questions和Threads_connected是即时值,Prometheus 有 15 秒抓取延迟,两边差一点正常。Questions是 Counter,只看单调递增,不要拿瞬时值做告警。如果两边差很多,先查 exporter 是否连到另一个实例,再查instance标签有没有重复,最后看 MySQL 是否发生过重启。
5.3 长期存储与降采样:别让高基数拖慢查询
MySQL 指标本身基数不高,但instance、env、role、app组合多了以后,长期存储也会膨胀。不要用动态 SQL 文本做标签,比如把update语句里的条件拼进标签,那会把时间序列基数打爆。长期方案通常是远程写入对象存储或专用时序库,再配降采样:原始 15 秒数据保留 15 天,5 分钟降采样保留 90 天,1 小时降采样保留 1 年。查询降采样数据时,rate窗口不要小于降采样间隔,否则会得到空值或锯齿。如果降采样后面板查询太慢,把 Grafana 查询的step显式写成1m,比依赖自动 step 更可控。
本文还有配套的精品资源,点击获取