一份实用的分步指南,教你在 Linux VPS 或云服务器上调优 MySQL 8.0:先用慢查询日志进行测量,正确配置 InnoDB 缓冲池大小,读懂 EXPLAIN 输出,并添加合适的索引。
MySQL 数据库性能调优基础
数据库慢是导致应用响应迟缓最常见的原因。在你扩容服务器或重写代码之前,花几个小时进行有条不紊的 MySQL 调优,往往就能找回大部分损失的性能。本指南将带你了解在运行 MySQL 8.0 的典型 Linux VPS 或云服务器上的调优基础:测量、调配内存、读懂查询计划,以及添加正确的索引。
这些步骤适用于任何标准的 MySQL 8.0 安装环境——包括托管在沙特境内 Skyline Cloud VPS 或云服务器上的数据库;将数据驻留在沙特阿拉伯境内,有助于满足 PDPL 和 NCA 合规要求。
前置条件
- 一台已安装 MySQL 8.0 的 Linux 服务器(Ubuntu 22.04/24.04、Debian 或 RHEL 系列)。
- 一个拥有
sudo权限的用户,以及一个具有管理员权限的 MySQL 账户。 - 真实或具有代表性的工作负载——针对空闲数据库进行调优毫无意义。
每次只更改一个变量,然后重新测量。没有测量的调优只是在瞎猜。
第 1 步——先用慢查询日志进行测量
你无法调优你没有测量过的东西。慢查询日志会记录每一条执行时间超过某一阈值的语句,这是找出最糟糕的性能瓶颈最快的方法。
在运行时启用它(无需重启):
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- log queries slower than 1 second
SET GLOBAL log_queries_not_using_indexes = 'ON';
要使其永久生效,请将以下内容添加到你的 MySQL 配置文件中(在 Ubuntu/Debian 上为 /etc/mysql/mysql.conf.d/mysqld.cnf,在 RHEL 上为 /etc/my.cnf):
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1
log_queries_not_using_indexes = 1
让一个具有代表性的工作负载运行一段时间,然后分析日志。Percona Toolkit 的 pt-query-digest 会按指纹将相同的查询归类,并按其消耗的总时间进行排序:
sudo apt install percona-toolkit # Debian/Ubuntu
pt-query-digest /var/log/mysql/mysql-slow.log | less
重点关注报告顶部的查询——也就是总执行时间最高的那些,而不仅仅是单次运行时间最高的。优化一条每分钟运行 10,000 次的查询,远比优化一条每晚才运行一次的查询更划算。
第 2 步——配置 InnoDB 缓冲池大小
InnoDB 是默认的存储引擎,它的缓冲池会将表数据和索引数据缓存在内存中。如果你的工作集能够装进缓冲池,MySQL 就能从内存而非磁盘提供大部分读取——这通常是单项收益最大的优化。
检查你当前的缓冲池大小和命中率:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SELECT
(1 - (SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_reads')
/ NULLIF((SELECT VARIABLE_VALUE FROM performance_schema.global_status
WHERE VARIABLE_NAME = 'Innodb_buffer_pool_read_requests'), 0)) * 100
AS hit_ratio_pct;
目标是让命中率达到 99% 或更高。根据 Oracle 的官方文档,在专用数据库服务器上,应将缓冲池设置为总内存的 50–75%,为操作系统和 MySQL 的其他缓冲区留出空间(InnoDB 本身会额外占用比配置值多出约 10% 的内存)。在小型共享服务器上,应将其控制在接近 50% 的水平,并为操作系统至少留出 1–2 GB。
对于一台 8 GB 的专用服务器,从大约 5–6 GB 开始是一个合理的起点:
[mysqld]
innodb_buffer_pool_size = 5G
重启 MySQL,并在工作负载预热缓存之后重新检查命中率。不要设置得过大:如果缓冲池加上其他进程超过了内存总量,操作系统就会换页到磁盘,性能将急剧下降。
第 3 步——用 EXPLAIN 读懂查询计划
在你知道哪些查询慢之后,要找出它们慢的_原因_。EXPLAIN 会显示 MySQL 打算如何执行一条语句:
EXPLAIN SELECT * FROM orders WHERE customer_id = 42 AND status = 'shipped';
最重要的几列:
| 列 | 需要关注什么 |
|---|---|
type |
ALL 表示全表扫描——通常是坏事。应优先选择 ref、range 或 const。 |
key |
使用了哪个索引。NULL 表示没有使用任何索引。 |
rows |
预计扫描的行数。越低越好。 |
Extra |
Using filesort 或 Using temporary 标志着代价高昂的排序步骤。 |
在 MySQL 8.0 中,你还可以更进一步使用 EXPLAIN ANALYZE,它会实际运行该查询,并在给出估算值的同时报告真实的耗时:
EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 42 AND status = 'shipped';
这是确认某项索引更改是否真正起到了作用最可靠的方法。
第 4 步——添加正确的索引
在大表上进行全表扫描(type: ALL)是导致查询缓慢的典型原因。请在 WHERE、JOIN 和 ORDER BY 子句所用到的列上添加索引。
对于上面那条查询,一个覆盖两个过滤列的复合索引能让 MySQL 直接跳转到匹配的行:
CREATE INDEX idx_orders_customer_status
ON orders (customer_id, status);
列的顺序很重要:把选择性最高的列——或者用于等值匹配的列——放在最前面。复合索引同样能为仅过滤其前导列(customer_id)的查询提供服务,因此你很少需要再为它单独建立一个单列索引。
重新运行 EXPLAIN ANALYZE,确认 type 是否有所改善(例如从 ALL 变为 ref),以及 rows 是否大幅下降。
索引并非没有代价——它们会占用磁盘空间,并拖慢写入操作(INSERT/UPDATE/DELETE),因为每个索引都必须被维护。要有针对性地建立索引,并移除那些你可以通过 sys.schema_unused_indexes 视图识别出来的未使用索引:
SELECT * FROM sys.schema_unused_indexes;
第 5 步——先验证,再迭代
调优是一个循环,而非一锤子买卖。每做一处更改之后:
- 用
EXPLAIN ANALYZE重新运行受影响的查询,并对比耗时。 - 再次让慢查询日志运行一段时间,并重新对其进行汇总分析(re-digest)。
- 确认缓冲池命中率依然保持在高位。
回滚任何没有起到作用的更改。记录下你更改了什么以及为什么这样改——未来的你会感谢现在的你。
常见陷阱
- 孤立地调优单条查询。 应按总体影响(频率 × 成本)来进行优化,而慢日志汇总分析正好能揭示这一点。
- 从博客里照搬
my.cnf的取值。 缓冲池大小取决于_你自己_的内存和数据集。要测量,不要照抄。 - 在被索引的列上套用函数。
WHERE DATE(created_at) = '2026-06-08'无法使用created_at上的索引;应改写为范围查询:WHERE created_at >= '2026-06-08' AND created_at < '2026-06-09'。 - 在宽表上使用
SELECT *。 只获取你需要的列,这样 MySQL 才能使用覆盖索引。
结语
MySQL 性能调优是有章可循的,而非什么玄学:用慢查询日志测量,给 InnoDB 足够的内存,用 EXPLAIN ANALYZE 读懂查询计划,并有针对性地添加索引。这四项基础功夫,能在你进行任何硬件升级之前,解决现实世界中绝大多数的性能问题。
对于需要扩展、需要可预期延迟或需要阿拉伯语本地支持的工作负载,请将你的数据库托管在沙特境内。了解 Skyline Cloud VPS 和云服务器,搭配企业邮箱托管以构建完整的技术栈,并创建你的 Skyline Cloud 账户,几分钟内即可部署一台经过调优的 MySQL 服务器。
Comments
0 total · 0 threads