说实话,每次看到生产环境的MySQL CPU突然飙到99%,或者应用响应时间从200ms变成20秒,那种感觉就像医生给病人做手术却找不到病灶在哪里。今天咱不聊虚的,直接给你一套能落地的监控方案,让你不仅能看到慢SQL,还能知道它为什么慢、怎么修。
先说说为什么你需要监控,而不是猜
很多开发者遇到性能问题时的第一反应是:换个索引吧,或者加条缓存。但这种做法就像不看X光片就开刀。我之前遇到过一位朋友,他的线上查询慢,他花了三天时间测试了十几个不同的索引组合,最后发现根本原因是表的设计缺陷——一个应该分表的业务被塞进了一个单表里。如果他有完善的监控数据,可能第一天就能定位问题。
性能监控的核心价值不在于“看到数据”,而在于建立因果关系。你需要知道:什么时候慢?哪里慢?为什么慢?PMM和MySQLTuner这两个工具,正好覆盖了这三个层面。
Percona Monitoring and Management (PMM):你的MySQL全景雷达
PMM是Percona开源的一套监控解决方案,它最大的特点是非侵入式和可视化强。不像Zabbix那种需要你在每台机器上装Agent的配置方式,PMM采用Client-Server架构,部署简单,数据展示直观。
架构核心:谁在收集,谁在展示?
PMM的架构分两个角色:
- PMM Server:负责存储数据、展示图表、发送告警。你可以把它理解为一个专门看监控的仪表盘。
- PMM Client:安装在数据库服务器上,负责收集指标并推送给Server。它通过
node_exporter收集系统级指标(CPU、内存、磁盘),通过mysqld_exporter收集MySQL专用指标(连接数、QPS、缓存命中率等)。
实战部署:Docker一键起服务
别被部署吓到,PMM官方提供了Docker镜像,一条命令就能跑起来。假设你有一台专门用于监控的虚拟机(配置建议:4核8G以上,磁盘至少50G):
# 1. 拉取PMM Server镜像
docker pull percona/pmm-server:2
# 2. 启动PMM Server容器
docker run -d \
--name pmm-server \
-p 443:443 \
-v /opt/consul-data:/opt/consul-data \
-v /usr/share/prometheus:/usr/share/prometheus \
--restart=always \
percona/pmm-server:2
启动后,浏览器访问https://你的服务器IP,默认用户名是admin,密码在容器日志里:
# 查看初始密码
docker logs pmm-server 2>&1 | grep -i password
连接MySQL并添加监控
PMM Server搭建好后,你需要在MySQL服务器上安装Client。这里有个关键点:PMM Client要装在数据库所在的机器上,而不是装在PMM Server上,这样收集的数据才是实时的本地数据。
# 在MySQL服务器上安装PMM Client(以CentOS为例)
yum install -y https://www.percona.com/redir/downloads/pmm2/redhat/latest/x86_64/rpms/pmm2-client-2.40.0-1.rhel7.x86_64.rpm
# 注册到PMM Server(替换为你的Server地址)
pmm-admin config --server-insecure-tls --server-url=https://admin:你查看到的密码@192.168.1.100:443
# 添加MySQL服务监控(替换为你的MySQL配置)
pmm-admin add mysql --user=root --password=你的MySQL密码 --host=127.0.0.1 --port=3306
添加成功后,你会看到类似这样的输出:
pmm-admin add mysql
ok
MySQL User: root
MySQL Host: 127.0.0.1
MySQL Port: 3306
Services added:
- mysql:metrics (exporter)
- mysql:queries (exporter)
回到PMM Web界面,你会看到数据库实例已经出现。点击进去,Query Analytics(QAN) 是最核心的功能,它能按时间、查询类型、耗时等维度展示所有SQL。
解读QAN:让慢SQL无处遁形
很多新手打开PMM,看到密密麻麻的图表就懵了。其实只需要关注三个维度:
Top Queries by Execution Time:这是最直观的,告诉你哪些SQL占用时间最长。注意,这里显示的是累积耗时,不是单次耗时。一条SQL每秒执行1000次,每次1ms,总耗时1秒;另一条SQL每秒执行1次,每次2秒,总耗时2秒。后者会排在前面,但这不代表前者不重要——前者可能正在拖垮你的数据库连接数。
Query Sample:点击任意一条SQL,你可以看到它的具体执行计划。PMM会调用
EXPLAIN并展示结果,这是诊断索引问题的神器。Filter by DB/User/Host:你可以按数据库名、用户名、来源IP过滤。比如你怀疑某个业务模块有问题,直接过滤对应的用户名,瞬间缩小范围。
举个实际例子:有一次线上报表查询慢,我在PMM里过滤了report_user这个数据库用户,发现有一条SELECT * FROM order_detail WHERE create_time BETWEEN '2024-01-01' AND '2024-12-31'的查询,每次执行耗时3秒,QPS只有0.5。看EXPLAIN结果,type是ALL(全表扫描),key是NULL(没用索引)。问题很明显:create_time字段没有索引。加索引后,查询耗时降到50ms。
MySQLTuner:一次性的深度体检
如果说PMM是长期的监控仪表盘,那MySQLTuner就是一次性的健康检查。它不会持续收集数据,而是读取MySQL当前的运行状态,分析配置参数是否合理,然后给出一堆建议。
为什么还需要MySQLTuner?
因为PMM解决的是实时问题,MySQLTuner解决的是配置问题。很多数据库性能差,不是因为某条SQL写得烂,而是因为innodb_buffer_pool_size只设了128M,或者max_connections配得太小。这些配置问题,PMM能告诉你“当前内存使用率很高”,但不会告诉你“建议把缓冲池调到8G”。
安装与运行
MySQLTuner是Perl脚本,安装非常简单:
# CentOS/RHEL
yum install -y perl perl-DBI perl-DBD-MySQL perl-Time-HiRes
# Ubuntu/Debian
apt-get install -y mysql-tuner
# 或者直接下载最新版
wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl
chmod +x mysqltuner.pl
运行命令也很简单:
./mysqltuner.pl --user root --password 你的密码
如何解读MySQLTuner的输出?
MySQLTuner的输出很长,但真正有用的只有三部分:Summary、Recommendations、Variables to Adjust。
Summary部分会给你一个整体评分(比如2/100,说明问题很大)和关键指标:
[--] Data in MyISAM tables: 15G (Tables: 25)
[--] Data in InnoDB tables: 85G (Tables: 150)
[!!] Uptime: 3600 sec
[!!] Total joins: 150000 (41/join)
[!!] Max used connections: 95 (95%)
注意这些[!!]标记,它们是警告项。Max used connections达到95%,说明连接数快爆了,需要调大max_connections或者优化应用层的连接池。
Recommendations部分是给具体建议,比如:
[!!] Combined from all working memory segments: 1.2G
[!!] RAM / Total = 56.25%
[!!] innodb_buffer_pool_size (64.0M) / Total Memory (2.0G) = 3.20%
这条建议的意思很明确:你的InnoDB缓冲池只用了总内存的3%,太少了!InnoDB是MySQL最常用的存储引擎,缓冲池负责缓存数据和索引,调太小会导致频繁的磁盘IO。一般建议设置为物理内存的70%-80%。
Variables to Adjust部分会给出可以直接执行的SQL:
SET GLOBAL innodb_buffer_pool_size=14398443520;
SET GLOBAL max_connections=200;
你可以直接复制这些SQL到MySQL里执行,让配置立即生效(注意:重启MySQL后配置会还原,要持久化需要改my.cnf)。
实战案例:MySQLTuner发现的隐藏问题
有一次,我帮一家电商公司优化订单系统。PMM监控显示QPS正常,连接数也没满,但订单查询经常超时。我运行MySQLTuner,发现一条关键建议:
[!!] table_open_cache (400) is less than number of tables (1024)
意思是:你同时打开的表数量超过了缓存上限,MySQL会频繁地打开和关闭表句柄,造成性能损耗。我查了代码,发现电商系统有上百个SKU相关表,设计不合理。最终建议是按商品类目分表,并调大table_open_cache到2000。这个问题PMM也能看到(Open_tables指标异常),但MySQLTuner直接告诉了你“该调什么参数”。
两者结合:构建完整的监控闭环
单独用PMM或单独用MySQLTuner,都有盲区。PMM擅长看实时趋势,MySQLTuner擅长看配置基线。把它们结合起来,才是完整的解决方案。
日常工作流建议
每天早上花5分钟看PMM的昨日概览:重点看QAN里的Top 10慢查询,确认有没有新出现的异常SQL。如果某条SQL的耗时突然飙升,立即排查。
每周运行一次MySQLTuner:对比上周的输出,看配置建议有没有变化。如果innodb_buffer_pool_size的建议从64M变成2G,说明数据库负载增加了,需要扩容。
发现慢SQL时的排查流程:
- 在PMM的QAN里定位具体SQL和执行时间
- 点击SQL查看EXPLAIN结果,分析全表扫描、临时表、文件排序等问题
- 如果怀疑是配置问题,运行MySQLTuner看有没有参数建议
- 修改配置或SQL后,在PMM里观察指标变化
一个真实的慢SQL排查全过程
去年我处理过一个案例:用户反馈App首页加载慢,后端日志显示MySQL查询平均耗时从50ms升到2秒。
第一步:PMM定位
打开PMM QAN,过滤homepage_db数据库,发现有一条查询:
SELECT * FROM user_profile WHERE last_login > '2024-01-01' ORDER BY score DESC LIMIT 10
这条SQL的累积耗时占比超过60%。点击查看详情,EXPLAIN结果显示:
id: 1
select_type: SIMPLE
table: user_profile
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 5000000
filtered: 10.00
Extra: Using where; Using filesort
全表扫描500万行,还要文件排序。问题很明显。
第二步:MySQLTuner辅助
运行MySQLTuner,发现sort_buffer_size建议值远低于当前值,但更重要的是:
[!!] tmp_table_size (16M) and max_heap_table_size (16M) seems to be small
虽然这不是主因,但说明临时表配置偏小,可能加剧性能问题。
第三步:优化实施 我加了两个索引:
-- 索引1:覆盖last_login过滤条件
ALTER TABLE user_profile ADD INDEX idx_last_login (last_login);
-- 索引2:覆盖排序字段,避免文件排序
ALTER TABLE user_profile ADD INDEX idx_last_login_score (last_login, score);
第四步:验证效果
重新运行EXPLAIN,type变成了range,key使用了idx_last_login_score,rows从500万降到5万,Extra里不再出现Using filesort。PMM里观察该SQL的平均耗时,从2秒降到80ms。
整个过程不到10分钟,如果没有任何监控工具,可能要在生产环境里猜上几天。
常见坑与避坑指南
坑1:PMM Server本身成为瓶颈 有些同学为了省事,把PMM Server部署在和MySQL同一个机器上。这是大忌!PMM的Prometheus和VictoriaMetrics会占用大量内存和磁盘IO,和MySQL抢资源。务必单独部署一台服务器。
坑2:MySQLTuner的误报
MySQLTuner的建议有时过于保守。比如它建议innodb_buffer_pool_size设为内存的70%,但如果你还有其他重型应用(比如Redis、Elasticsearch)在同一台机器上,这个建议就不合理了。永远要结合实际情况判断。
坑3:忽略慢日志的长期趋势 PMM的QAN能展示历史数据,但很多同学只看当天的数据。慢SQL问题往往是累积的,一条SQL上周耗时100ms,这周变成500ms,如果你只看今天,可能发现不了问题。建议设置告警:当某SQL的P99耗时超过阈值时,自动通知。
坑4:过度依赖自动化工具 工具只是辅助,不能替代思考。PMM显示某SQL慢,你要问自己:为什么慢?是数据量增长了?还是SQL写法变了?还是索引失效了?工具给你数据,你给决策。
总结:监控不是为了看,是为了行动
最后说句实在话:监控工具再好,如果不看、不用,就是摆设。PMM和MySQLTuner的价值不在于你部署了多少服务器、采集了多少指标,而在于你在问题发生前发现了它。
建议你从今天开始:
- 部署PMM,接入核心数据库
- 每周运行MySQLTuner,记录关键配置变化
- 建立慢SQL响应机制:发现后1小时内定位,24小时内优化
当你养成这个习惯,你会发现MySQL的性能问题不再是“突发灾难”,而是“可预测、可管理”的日常事务。毕竟,最好的运维,是让问题在用户感知之前就消失。
