TIL: MySQL 慢了从哪查起——六个工具的排查顺序
TecMint 上有篇 文章 介绍了六个 MySQL 性能监控的命令行工具。工具清单不难搜到,但这几个工具恰好覆盖了从"接到投诉"到"验证修复"的完整排查链路,理成工作流比单独学每个工具有用。
是什么
数据库慢了怎么查?我的习惯是先看基线数字,判断是不是真的在变慢;再看实时状态,确认现在正在发生什么;如果线程正常但查询就是慢,用 InnoDB 诊断工具看看是不是引擎层的问题;接着翻慢查询日志,揪出拖后腿的 SQL;然后检查配置参数有没有不合理的;改完东西之后跑一轮压测,确认改动生效。
| 步骤 | 工具 | 关键指标 |
|---|---|---|
| 基线检查 | mysqladmin |
Slow queries 累计数、Threads 数 |
| 实时监控 | mytop |
qps now(实时每秒查询数)、正在跑的 SQL |
| InnoDB 诊断 | innotop |
Buffer Pool 命中率、锁等待 |
| 慢查询定位 | pt-query-digest |
Response time 占比最高的查询 |
| 配置检查 | MySQLTuner |
[!!] 标记的参数问题 |
| 压测验证 | mysqlslap |
改前改后的平均耗时对比 |
这些步骤之间有依赖关系。比如你不知道基线的 Slow queries 是多少,就没法判断慢查询是不是在加速增长。 mytop 看到线程数正常但查询就是慢,就轮到 innotop 看是不是 InnoDB 层的问题(缓冲池不够或者锁竞争)。不开慢查询日志的话, pt-query-digest 也没数据可吃。调完参数不压测,等于盲改。
为什么值得关注
排查 MySQL 性能问题时特别容易犯的毛病就是跳步,一上来就改 innodb_buffer_pool_size ,或者到处加索引,但你还没搞清楚到底是查询慢还是连接数爆了。按依赖顺序排查,每步只盯一两个关键指标,能少走弯路。
另外有个坑,这几个工具的安装方式差别挺大。 mysqladmin 和 mysqlslap 是 MySQL 自带的, mytop 原版 2009 年就停更了得找社区 fork, innotop 大部分发行版有包但名字不一样, pt-query-digest 要加 Percona 仓库, MySQLTuner 倒是省事,单个 Perl 脚本下载就能跑。记住哪个工具怎么装、该看什么,比死记参数管用。
怎么用
第一步:记基线
mysqladmin 是 MySQL 自带的管理工具,不用额外安装:
mysqladmin -u root -p status
Uptime: 475424 Threads: 9 Questions: 2491823 Slow queries: 14 Opens: 412 ...
记住 Slow queries 的值。这个数字只增不减(服务器重启才归零),过一小时再查一次,从 14 涨到 140 就说明这一小时出了问题。 Threads 数如果逼近 max_connections 上限,新连接会被直接拒绝。
持续观察加 -i 2 每 2 秒刷新:
mysqladmin -u root -p -i 2 status
第二步:看实时
mytop 提供类似 Linux top 的实时视图,能看到每秒查询数和正在跑的 SQL:
mytop -u root -p YourPassword -h localhost
mytop 的 -p 后面必须跟密码,不像 mysql 那样单独写 -p 会弹出交互提示。嫌命令行暴露密码的话,可以在 ~/.mytop 里写配置:
user=root pass=YourPassword host=localhost db=mysql
写完后记得 chmod 600 ~/.mytop ,否则别的用户能读到密码。
MySQL on localhost (8.0.36) up 5+12:03:44 [12:45:01]
Queries: 2.5M qps: 87 Slow: 14 Se/In/Up/De(%): 62/18/14/06
qps now: 92 Slow qps: 0.3 Threads: 9 ( 8/ 1)
Id User Host/IP DB Time Cmd Query or State
112 app_user 192.168.1.45 mydb 0 Query SELECT * FROM orders WHERE...
qps now 是实时的每秒查询数。这个数字飙升但应用响应变慢,就是负载问题。下半部分能看到哪个用户在跑什么 SQL。
注意,原版 mytop 2009 年停更, apt install mytop 在 MySQL 8 上装不上。要用社区 fork( fevangelou/mytop ),安装步骤:
# Ubuntu/Debian sudo apt install libdbi-perl libdbd-mysql-perl libterm-readkey-perl cd ~ && git clone https://github.com/fevangelou/mytop.git cd mytop && chmod +x mytop sudo cp mytop /usr/local/sbin/ # RHEL/Rocky Linux sudo dnf install perl-DBI perl-DBD-MySQL perl-TermReadKey cd ~ && git clone https://github.com/fevangelou/mytop.git cd mytop && chmod +x mytop sudo cp mytop /usr/local/sbin/
第三步:看 InnoDB 内部状态
mytop 看的是线程和查询列表,但如果线程数正常、查询也跑着就是慢,问题大概率在 InnoDB 引擎层。 innotop 能看到 InnoDB 的缓冲池状态、锁等待和事务详情。
大部分发行版有包,直接装:
sudo apt install innotop # Ubuntu/Debian sudo dnf install innotop # RHEL/Rocky Linux sudo pacman -S innotop # Arch Linux
如果仓库里没有,从 GitHub 装也行: git clone https://github.com/innotop/innotop.git 。装好后连接:
innotop -u root -p
[RO] InnoDB Txns, InnoDB 8.0.36, 9 threads, 92.0 QPS CXN Timeouts Deadlocks Txns RollSegments History local 0 0 3 128 42 ID User Host DB Time Undo Query 42 app_user local mydb 3 18 UPDATE inventory SET qty=qty-1 WHERE...
重点盯两个东西。第一个是 Deadlocks 列,只要不是 0 就说明有事务在互相等锁,需要排查哪两个 SQL 在争同一行。第二个是按 B 键切换到 Buffer Pool 视图,看缓冲池命中率。命中率低于 99% 说明内存不够用,数据在走磁盘而不是走内存,这时候调大 innodb_buffer_pool_size 比优化 SQL 更管用。
第四步:定位慢查询
先确保慢查询日志开着(超过 1 秒的查询记下来)。
mysql -u root -p -e "SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1;"
注意 SET GLOBAL 只对当前运行实例生效,MySQL 重启后失效。要持久化的话写进 my.cnf :
[mysqld] slow_query_log = ON long_query_time = 1 slow_query_log_file = /var/log/mysql/mysql-slow.log
然后从 Percona Toolkit 安装 pt-query-digest :
# Ubuntu/Debian curl -O https://repo.percona.com/apt/percona-release_latest.generic_all.deb sudo apt install ./percona-release_latest.generic_all.deb sudo percona-release enable pt release sudo apt update && sudo apt install percona-toolkit # RHEL/Rocky Linux sudo yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm sudo percona-release enable pt release sudo yum install percona-toolkit
装好后分析慢查询日志:
pt-query-digest /var/log/mysql/mysql-slow.log
# Overall: 184 total, 8 unique, 0.02 QPS, 0.14x concurrency # Attribute total min max avg 95% stddev median # Exec time 512s 1s 143s 3s 9s 14s 1s # Profile # Rank Query ID Response time Calls R/Call Item # 1 0x813031B8BBC3B329 341s 66.6% 42 8.12s SELECT orders items # 2 0xA0CA1BBEDCD1B80D 108s 21.1% 89 1.21s SELECT users sessions
Response time 的百分比直接告诉你优先级。Rank 1 占了 66% 的慢查询时间,跑了 42 次平均每次 8 秒。加个索引或者改写 JOIN,就能砍掉一大半的慢查询负担。不用把 8 个慢查询都修一遍,修占比最高的那一两条就够了。
同属 Percona Toolkit 的 pt-mysql-summary 适合在排查前先跑一遍,了解全局状态:
pt-mysql-summary -- -u root -p
它会一次性输出 MySQL 版本、运行端口、数据目录、当前进程列表( Command 列按 Query/Sleep 分类统计)、缓冲池使用率、复制状态等。其中 Processlist 部分能看到 Sleep 连接占用了多少时间。如果 Sleep 的 SUM(Time) 很高,说明大量空闲连接没关,是连接池配置的问题而不是查询的问题。第一次登录一台不熟悉的服务器时跑一遍,能快速判断方向。
第五步:检查配置
MySQLTuner 是单个 Perl 脚本,下载就能跑:
wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl perl mysqltuner.pl --user root --ask-pass
用 --ask-pass 交互输入密码,避免密码留在 shell 历史里。
[!!] InnoDB buffer pool / data size: 128M / 2G
[!!] InnoDB buffer pool instances: 1 (recommended 2)
General recommendations:
Increase innodb_buffer_pool_size to 1.5G or more
Add innodb_buffer_pool_instances = 2 to my.cnf
[!!] 标记的是需要关注的问题。上面这个输出说的是,缓冲池(InnoDB 用来缓存数据和索引的内存区域)只有 128MB,但实际数据有 2GB,大量读操作在走磁盘而不是走内存。把 innodb_buffer_pool_size 设到可用内存的 70% 左右,新服务器上这一步改下来效果通常最明显。
第六步:验证效果
改完配置后用 mysqlslap (MySQL 自带)跑基准测试,先记下改前的数字,改完再跑一次对比:
mysqlslap -u root -p --concurrency=50 --iterations=1 \
--auto-generate-sql --number-of-queries=200
Benchmark
Average number of seconds to run all queries: 3.847 seconds
Number of clients running queries: 50
Average number of queries per client: 4
记下 Average number of seconds 。加索引后再跑,从 3.8 秒降到 1.2 秒就说明有效。也可以指定真实 SQL 来测特定查询:
mysqlslap -u root -p --concurrency=25 --iterations=3 \ --query="SELECT * FROM mydb.orders WHERE status='pending'" \ --create-schema=mydb