一、MySQLTuner 是什么?
如果你管理过 MySQL 数据库,一定遇到过这样的场景:线上慢查询突然增多,CPU 使用率飙到 80%,但你打开 my.cnf 看了半天也不知道该调哪个参数。这时候就需要一个"老中医"帮你把脉——MySQLTuner 就是这样一款工具。
MySQLTuner 是一个用 Perl 编写的开源脚本,托管在 GitHub 上,目前已经积累了超过 9000 个 star。它的核心思路非常简单:连接到你的 MySQL 实例,读取 SHOW GLOBAL STATUS、SHOW GLOBAL VARIABLES 等信息,然后根据内置的规则引擎给出优化建议。
它的优势在于:
- 零依赖部署:只要有 Perl 环境(几乎所有 Linux 发行版都自带),下载一个脚本文件就能跑。
- 只读操作:不会修改任何配置,完全安全。
- 报告直观:用颜色标记 OK / WARNING / FAIL,一眼就能看到问题。
- 持续维护:社区活跃,规则库一直在更新,支持 MySQL 5.7、8.0、8.4 以及 MariaDB。
一句话总结:MySQLTuner 就是 MySQL 世界的"体检报告"。
二、安装 MySQLTuner
安装 MySQLTuner 有好几种方式,选最适合你环境的就行。
2.1 wget 一键下载(推荐)
这是最快的方式,一条命令搞定:
1# 下载最新版本
2
3> **本章目标**:掌握本章核心知识点
4> **前置要求**:完成前序章节学习
5> **预计时长**:60 分钟
6
7wget https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl -O /usr/local/bin/mysqltuner.pl
8
9# 赋予执行权限
10chmod +x /usr/local/bin/mysqltuner.pl
如果你的服务器无法访问 GitHub,可以先在本地下载,再通过 scp 传过去:
1scp mysqltuner.pl root@your-server:/usr/local/bin/
2.2 Git Clone 完整仓库
如果你想保留完整的项目文件(包括文档、CVE 漏洞数据库等),可以 clone 整个仓库:
1git clone https://github.com/major/MySQLTuner-perl.git
2cd MySQLTuner-perl
3perl mysqltuner.pl
这种方式的好处是可以随时 git pull 获取最新规则。
2.3 包管理器安装
部分 Linux 发行版的官方仓库已经收录了 MySQLTuner:
1# Debian / Ubuntu
2sudo apt-get install mysqltuner
3
4# CentOS / RHEL (需要 EPEL 源)
5sudo yum install epel-release
6sudo yum install mysqltuner
7
8# Arch Linux
9sudo pacman -S mysqltuner
注意:包管理器里的版本通常比 GitHub 上的旧一些。如果你需要最新的规则和修复,建议用 wget 方式。
2.4 验证安装
无论用哪种方式安装,都可以用以下命令确认:
1perl /usr/local/bin/mysqltuner.pl --help
看到帮助信息就说明一切正常。
三、基本用法与常用参数
3.1 最简运行
在 MySQL 所在的服务器上,直接运行:
脚本会尝试通过 socket 连接本机的 MySQL,如果当前用户有 .my.cnf 配置文件,甚至不需要输入密码。
3.2 指定连接信息
连接远程 MySQL 或指定用户名密码:
1perl mysqltuner.pl \
2 --host 192.168.1.100 \
3 --port 3306 \
4 --user root \
5 --pass 'YourP@ssw0rd'
安全提示:在命令行里直接写密码会被记录到 bash_history。生产环境建议使用 --forcemem 和 --forceswap 参数并通过 .my.cnf 文件传递凭据。
3.3 关键参数详解
| 参数 | 说明 | 示例 |
|---|
--host | MySQL 服务器地址 | --host 10.0.0.5 |
--port | 端口号,默认 3306 | --port 3307 |
--user | 连接用户名 | --user dba_user |
--pass | 连接密码 | --pass 'xxx' |
--forcemem | 强制指定总内存(MB) | --forcemem 8192 |
--forceswap | 强制指定 swap 大小(MB) | --forceswap 4096 |
--skipsize | 跳过表大小统计(大库加速) | --skipsize |
--json | 以 JSON 格式输出 | --json |
--outputfile | 将报告写入文件 | --outputfile /tmp/report.txt |
--cvefile | 指定 CVE 漏洞数据文件 | --cvefile vulnerabilities.csv |
--defaults-file | 指定 my.cnf 路径 | --defaults-file /etc/mysql/my.cnf |
3.4 典型运行命令
生产环境推荐的完整运行命令:
1perl mysqltuner.pl \
2 --host 127.0.0.1 \
3 --user tuner_user \
4 --pass 'SecurePass123!' \
5 --forcemem 16384 \
6 --forceswap 8192 \
7 --outputfile /var/log/mysqltuner/report_$(date +%Y%m%d).txt
建议为 MySQLTuner 创建一个专用的只读账号:
1CREATE USER 'tuner_user'@'localhost' IDENTIFIED BY 'SecurePass123!';
2GRANT SELECT, PROCESS, REPLICATION CLIENT ON *.* TO 'tuner_user'@'localhost';
3FLUSH PRIVILEGES;
这样既能获取足够的信息,又不会给予多余的权限。
四、报告各板块详解
MySQLTuner 的报告分为多个板块,每个板块用不同颜色标记状态。下面逐一讲解。
4.1 General Statistics(基本统计)
这是报告开头的部分,显示 MySQL 的版本、运行时间、连接数等基础信息:
1-------- General Statistics ------------------------------------------------
2[--] Skipped version check for MySQLTuner script
3[OK] Currently running supported MySQL version 8.0.36
4[OK] Operating on 64-bit architecture
5[OK] Uptime: 45d 12h 30m (3931800 seconds)
6[OK] Total connections: 1523456
7[OK] Avg connections per second: 0.387
关注点:
- Uptime:运行时间越长,统计数据越有参考价值。刚重启的实例建议等几天再跑巡检。
- 连接数趋势:如果平均每秒连接数很高,可能需要考虑连接池。
4.2 Storage Engine Statistics(存储引擎统计)
1-------- Storage Engine Statistics -----------------------------------------
2[--] Data in InnoDB tables: 125.6G (Tables: 342)
3[--] Data in MyISAM tables: 256.0M (Tables: 15)
4[OK] Total fragmented tables: 3
关注点:
- 如果还有大量 MyISAM 表,强烈建议迁移到 InnoDB(除非有明确的理由)。
- 碎片化表数量多的话,需要定期执行
OPTIMIZE TABLE。
这是报告的核心部分,涵盖了多个子项:
1-------- Performance Metrics -----------------------------------------------
2[OK] Total buffers: 2.0G global + 16.5M per thread (151 max threads)
3[OK] Query cache: disabled
4[!!] Sorts requiring temporary tables: 12% (> 10%)
5[!!] Joins performed without indexes: 1523
6[OK] Temporary tables created on disk: 5% (< 25%)
7[!!] Thread cache hit rate: 85% (< 90%)
关注点:
- Sorts requiring temporary tables:排序用到了临时表,通常意味着
sort_buffer_size 偏小或者查询本身需要优化。
- Joins without indexes:没有索引的 JOIN 操作会全表扫描,是慢查询的重灾区。
- Thread cache hit rate:低于 90% 说明线程创建/销毁太频繁,需要增大
thread_cache_size。
4.4 InnoDB Status(InnoDB 状态)
1-------- InnoDB Metrics ----------------------------------------------------
2[OK] InnoDB File per table: ON
3[OK] InnoDB buffer pool / data size: 8.0G / 5.2G
4[OK] InnoDB buffer pool hit rate: 99.8%
5[!!] InnoDB log waits: 12 (> 0)
6[OK] InnoDB buffer pool instances: 8
关注点:
- Buffer pool hit rate:低于 99% 就需要认真考虑增大
innodb_buffer_pool_size。
- InnoDB log waits:大于 0 表示 redo log 写入出现了等待,可能需要增大
innodb_log_file_size。
- File per table:MySQL 8.0 默认开启,如果是旧版升级上来的需要确认。
4.5 Security Recommendations(安全建议)
1-------- Security Recommendations ------------------------------------------
2[!!] User 'app_user'@'%' has wildcard host access
3[!!] User 'root'@'%' has no password
4[OK] No anonymous accounts found
5[!!] 3 users have SUPER privilege
6[!!] CVE-2024-XXXX: Upgrade to MySQL 8.0.37 or later
关注点:
- 通配符主机:
'%' 意味着从任意 IP 都能连接,生产环境应该限制为具体 IP 或网段。
- 无密码账户:这是最低级但也最危险的安全隐患。
- SUPER 权限:应该遵循最小权限原则,只给真正需要的人。
- CVE 漏洞:如果扫描出已知漏洞,需要尽快安排版本升级。
五、常见问题及修复方案
MySQLTuner 跑完以后,最常见的几个告警以及对应的解决方案如下。
5.1 innodb_buffer_pool_size 太小
告警示例:
1[!!] InnoDB buffer pool / data size: 128.0M / 12.5G
2[!!] InnoDB buffer pool hit rate: 92.3% (< 95%)
分析:InnoDB Buffer Pool 是 MySQL 最重要的内存区域,用来缓存数据页和索引页。如果它比你的数据量小很多,就会频繁地从磁盘读取,性能自然上不去。
修复方案:
一般建议设置为物理内存的 50%~70%(前提是这台机器专跑 MySQL):
1# /etc/mysql/mysql.conf.d/mysqld.cnf
2[mysqld]
3innodb_buffer_pool_size = 8G
4innodb_buffer_pool_instances = 8
如果你不想重启 MySQL,8.0 以上版本支持在线调整:
1SET GLOBAL innodb_buffer_pool_size = 8589934592; -- 8G
小贴士:调整后观察 Innodb_buffer_pool_read_requests 和 Innodb_buffer_pool_reads 的比值,命中率应该在 99% 以上。
5.2 table_open_cache 需要增大
告警示例:
1[!!] Table cache hit rate: 58% (1200 open / 2048 opened)
分析:每次打开一个表都需要消耗文件描述符和内存。如果 cache 太小,MySQL 会频繁关闭和重新打开表,增加开销。
修复方案:
1[mysqld]
2table_open_cache = 4096
3table_open_cache_instances = 16
同时需要确保操作系统的文件描述符上限足够:
1# 查看当前限制
2ulimit -n
3
4# 临时调整
5ulimit -n 65535
6
7# 永久调整,编辑 /etc/security/limits.conf
8mysql soft nofile 65535
9mysql hard nofile 65535
5.3 slow_query_log 未开启
告警示例:
1[!!] Slow queries log is NOT enabled.
分析:慢查询日志是排查性能问题的第一手资料。不开启等于盲人摸象。
修复方案:
1[mysqld]
2slow_query_log = 1
3slow_query_log_file = /var/log/mysql/slow.log
4long_query_time = 1
5log_queries_not_using_indexes = 1
也可以在线开启(不需要重启):
1SET GLOBAL slow_query_log = 'ON';
2SET GLOBAL long_query_time = 1;
3SET GLOBAL log_queries_not_using_indexes = 'ON';
开启后配合 pt-query-digest 工具分析慢日志,效果更佳:
1pt-query-digest /var/log/mysql/slow.log > /tmp/slow_report.txt
5.4 tmp_table_size / max_heap_table_size 调整
告警示例:
1[!!] Temporary tables created on disk: 35% (> 25%)
分析:当临时表的大小超过 tmp_table_size 或 max_heap_table_size(取两者的较小值)时,MySQL 会把临时表写入磁盘,性能会显著下降。
修复方案:
1[mysqld]
2tmp_table_size = 256M
3max_heap_table_size = 256M
注意:这两个参数必须设置成一样的值,因为 MySQL 取两者的最小值作为实际上限。另外也不要设得太大——每个连接都可能分配一个临时表,连接数多的情况下可能导致内存不足。
5.5 其他常见告警速查表
| 告警 | 原因 | 修复建议 |
|---|
Key buffer used < 50% | MyISAM key buffer 利用率低 | 如果没有 MyISAM 表,减小 key_buffer_size |
Max connections ever used > 85% | 连接数接近上限 | 增大 max_connections 或引入连接池 |
Binary log enabled but not backed up | binlog 未及时清理 | 设置 expire_logs_days = 7 |
Thread cache hit rate < 90% | 线程频繁创建销毁 | 增大 thread_cache_size |
Query cache fragmentation > 20% | 查询缓存碎片化严重 | MySQL 8.0 已移除查询缓存,无需处理 |
六、自动化定期巡检(Cron + 邮件通知)
手动跑 MySQLTuner 适合临时排查,但在生产环境中,我们更需要的是定期自动巡检。下面介绍如何用 cron 实现。
6.1 创建巡检脚本
1#!/bin/bash
2# /opt/scripts/mysql_inspect.sh
3# MySQL 自动巡检脚本
4
5REPORT_DIR="/var/log/mysqltuner"
6DATE=$(date +%Y%m%d_%H%M)
7REPORT_FILE="${REPORT_DIR}/report_${DATE}.txt"
8EMAIL="dba-team@your-company.com"
9
10# 确保目录存在
11mkdir -p ${REPORT_DIR}
12
13# 运行 MySQLTuner
14perl /usr/local/bin/mysqltuner.pl \
15 --host 127.0.0.1 \
16 --user tuner_user \
17 --pass 'SecurePass123!' \
18 --forcemem 16384 \
19 --forceswap 8192 \
20 --outputfile ${REPORT_FILE} \
21 --nogood \
22 2>&1
23
24# 检查是否有警告项
25WARNING_COUNT=$(grep -c '\[!!\]' ${REPORT_FILE})
26
27# 根据告警数量决定邮件主题
28if [ ${WARNING_COUNT} -gt 0 ]; then
29 SUBJECT="[WARNING] MySQL 巡检发现 ${WARNING_COUNT} 个问题 - $(hostname) - ${DATE}"
30else
31 SUBJECT="[OK] MySQL 巡检正常 - $(hostname) - ${DATE}"
32fi
33
34# 发送邮件
35mail -s "${SUBJECT}" ${EMAIL} < ${REPORT_FILE}
36
37# 清理 30 天前的旧报告
38find ${REPORT_DIR} -name "report_*.txt" -mtime +30 -delete
39
40echo "[$(date)] 巡检完成,报告已保存至 ${REPORT_FILE}"
别忘了加执行权限:
1chmod +x /opt/scripts/mysql_inspect.sh
6.2 配置 Cron 定时任务
1# 编辑 crontab
2crontab -e
3
4# 每周一早上 6 点执行巡检
50 6 * * 1 /opt/scripts/mysql_inspect.sh >> /var/log/mysqltuner/cron.log 2>&1
6
7# 如果是核心业务库,可以每天跑一次
80 6 * * * /opt/scripts/mysql_inspect.sh >> /var/log/mysqltuner/cron.log 2>&1
6.3 JSON 输出 + 自定义告警
如果你有自己的监控平台(比如 Grafana + Prometheus,或者企业微信/钉钉告警),可以用 JSON 输出格式接入:
1perl mysqltuner.pl \
2 --host 127.0.0.1 \
3 --user tuner_user \
4 --pass 'SecurePass123!' \
5 --json \
6 --outputfile /tmp/mysqltuner.json
然后用 Python 或 shell 脚本解析 JSON,提取关键指标推送到告警系统:
1# 简单示例:提取告警数量并推送到企业微信
2WARNING_COUNT=$(cat /tmp/mysqltuner.json | python3 -c "
3import sys, json
4data = json.load(sys.stdin)
5warnings = [r for r in data.get('Recommendations', []) if 'adjust' in r.lower() or 'increase' in r.lower()]
6print(len(warnings))
7")
8
9if [ "${WARNING_COUNT}" -gt 0 ]; then
10 curl -s -X POST "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY" \
11 -H 'Content-Type: application/json' \
12 -d "{\"msgtype\":\"text\",\"text\":{\"content\":\"MySQL巡检告警:发现 ${WARNING_COUNT} 个需要关注的项,请查看邮件报告。\"}}"
13fi
七、MySQLTuner vs OraCheck:双引擎巡检体系
很多企业同时使用 Oracle 和 MySQL 两种数据库。Oracle 官方提供了一个叫 OraCheck(现在改名叫 Autonomous Health Framework / AHF)的巡检工具,和 MySQLTuner 定位类似但针对 Oracle 生态。
7.1 对比一览
| 维度 | MySQLTuner | OraCheck / AHF |
|---|
| 目标数据库 | MySQL / MariaDB | Oracle Database / RAC / ASM |
| 开发语言 | Perl | Python / Shell |
| 授权方式 | 开源(GPL) | Oracle 官方提供(需 MOS 账号下载) |
| 安装复杂度 | 极低(单文件) | 中等(需要解压安装) |
| 输出格式 | 文本 / JSON | HTML 报告 |
| 安全检查 | 用户权限、CVE 漏洞 | CIS Benchmark、补丁检查 |
| 性能调优 | 内存、缓存、引擎参数 | SGA/PGA、等待事件、AWR |
| 自动修复 | 不支持(只给建议) | 部分支持(autofix 模式) |
7.2 构建双引擎巡检流程
如果你的公司同时管理 Oracle 和 MySQL,可以这样组织巡检体系:
这样做的好处是:统一巡检节奏,统一告警通道,统一报告格式。DBA 团队不需要分别维护两套流程。
7.3 互补之处
- MySQLTuner 擅长参数调优建议,会直接告诉你某个参数应该设成多少。
- OraCheck 擅长最佳实践审计,会对照 Oracle 的官方建议逐项检查。
- 两者都可以脚本化、定时化、报告化。
- 两者都是只读操作,不会对数据库造成任何影响。
八、实战案例:生产服务器优化全过程
下面分享一个真实的生产环境案例(已脱敏),展示如何根据 MySQLTuner 的报告一步步优化 MySQL 性能。
8.1 背景
- 服务器配置:CentOS 7,32G 内存,8 核 CPU,SSD 磁盘
- MySQL 版本:8.0.32
- 业务场景:电商订单系统,日均写入 500 万行,读写比约 7:3
- 症状:下午高峰期 RT(响应时间)从 20ms 飙到 200ms+,偶发超时
8.2 MySQLTuner 报告关键告警
运行 MySQLTuner 后,得到了以下告警:
1[!!] InnoDB buffer pool / data size: 2.0G / 28.5G
2[!!] InnoDB buffer pool hit rate: 94.2% (< 95%)
3[!!] InnoDB log waits: 156 (> 0)
4[!!] Slow queries: 12% (2345 / 19523)
5[!!] Sorts requiring temporary tables: 18% (> 10%)
6[!!] Temporary tables created on disk: 32% (> 25%)
7[!!] Table cache hit rate: 62% (512 open / 825 opened)
8[!!] Thread cache hit rate: 78% (< 90%)
9[!!] Slow queries log is NOT enabled.
一共 8 个严重告警,问题还不少。
8.3 优化步骤
第一步:扩大 InnoDB Buffer Pool
数据量 28.5G,但 buffer pool 只有 2G,命中率只有 94.2%——这意味着大量数据需要从磁盘读取。
1-- 在线调整到 20G(服务器总内存 32G 的 62.5%)
2SET GLOBAL innodb_buffer_pool_size = 21474836480;
同时写入配置文件,防止重启丢失:
1[mysqld]
2innodb_buffer_pool_size = 20G
3innodb_buffer_pool_instances = 8
第二步:增大 InnoDB Redo Log
156 次 log waits 说明 redo log 太小,写入繁忙时会阻塞:
1[mysqld]
2innodb_log_file_size = 1G
3innodb_log_files_in_group = 3
注意:修改 redo log 大小需要重启 MySQL。建议在低峰期操作。
第三步:开启慢查询日志并优化 SQL
1SET GLOBAL slow_query_log = 'ON';
2SET GLOBAL long_query_time = 0.5;
3SET GLOBAL log_queries_not_using_indexes = 'ON';
开启后收集了两天的慢日志,用 pt-query-digest 分析:
1pt-query-digest /var/log/mysql/slow.log --limit 20
发现排名前三的慢查询:
- 一个订单列表查询没有走索引——加了组合索引后从 800ms 降到 5ms。
- 一个统计报表查询每次全表扫描 2000 万行——改成按天分区后降到 50ms。
- 一个 JOIN 查询关联了 5 张表——拆分为两次查询后降到 30ms。
第四步:调整临时表和缓存参数
1[mysqld]
2tmp_table_size = 256M
3max_heap_table_size = 256M
4table_open_cache = 4096
5table_open_cache_instances = 16
6thread_cache_size = 64
8.4 优化效果
一周后再次运行 MySQLTuner,对比结果如下:
| 指标 | 优化前 | 优化后 |
|---|
| Buffer pool hit rate | 94.2% | 99.7% |
| InnoDB log waits | 156 | 0 |
| 慢查询比例 | 12% | 0.3% |
| 磁盘临时表比例 | 32% | 4% |
| Table cache hit rate | 62% | 97% |
| Thread cache hit rate | 78% | 99% |
| 高峰期平均 RT | 200ms+ | 15ms |
所有告警清零,高峰期 RT 从 200ms 降到了 15ms。整个过程没有升级硬件,纯靠参数调优和 SQL 优化。
九、总结与建议
MySQLTuner 是每个 MySQL DBA 工具箱里应该有的工具。总结几个关键建议:
- 新实例上线后至少运行一周再巡检,这样统计数据才有参考价值。
- 不要盲目照搬建议。MySQLTuner 给出的是通用建议,你需要结合自己的业务场景做判断。比如它可能建议你增大
max_connections,但真正该做的是引入连接池。
- 定期巡检比临时巡检更重要。问题是慢慢积累的,定期巡检能在问题爆发前发现苗头。
- 巡检只是起点。发现问题后,还需要结合慢日志分析、
EXPLAIN 执行计划、SHOW ENGINE INNODB STATUS 等手段深入排查。
- 双引擎环境下,MySQLTuner + OraCheck 组合能覆盖大部分数据库健康检查需求。
最后,附上几个有用的链接:
希望这篇教程对你有帮助。如果你的 MySQL 服务器也有性能问题,不妨现在就跑一次 MySQLTuner 试试吧。
本章小结
本章介绍了以下核心内容:
- 一、MySQLTuner 是什么?
- 二、安装 MySQLTuner
- 三、基本用法与常用参数
- 四、报告各板块详解
- 五、常见问题及修复方案
- 六、自动化定期巡检(Cron + 邮件通知)
- 七、MySQLTuner vs OraCheck:双引擎巡检体系
- 八、实战案例:生产服务器优化全过程
- 九、总结与建议