site logo

Marico's space

MySQL 8.4 性能调优 – 完整指南 2026

算法解析 2026-06-30 11:29:26 7

最近折腾 MySQL 8.4,在几套生产环境里踩了几个坑,这篇把问题说清楚,不整虚的。

MySQL 依然是国内最流行的数据库之一,从电商、ERP到各种后台系统,到处都在跑。但说实话,我见过太多服务器配置拉满却还是慢得像蜗牛的情况——问题往往不在硬件,在于那些没人管的配置和SQL。

MySQL 8.4 LTS 确实带来了不少改进:优化器更聪明了、统计信息管理更完善、对JSON的支持也顺手多了。但现实是,绝大多数性能问题跟版本没关系。

改配置、调索引、加监控——这篇把实操经验都摊开讲。

什么时候该做性能调优?

CPU 高不代表要加内存,磁盘飘红也不一定是 IO 的锅。我见过好几台高配机器资源利用率低得可怜,原因就是几个慢查询把整个库拖下水。

下面这些症状出现超过两个,基本可以确定有优化空间:

  • 白天大部分时间 CPU 占用超过 70%
  • 查询响应时间动不动就超过 500 毫秒
  • 磁盘 IO 居高不下
  • 主从延迟一直飘红
  • 锁等待频繁出现
  • 死锁时有发生
  • 硬件不差但应用就是慢
  • 表数据膨胀速度异常
  • Buffer Pool 命中率低于 98%

踩中三条以上的话,这篇文章你算是来对了。

第一步:搞清楚你的 workload

很多人一上来就改 my.cnf,改完发现毛用没有——因为根本不了解业务怎么用数据库。

调参之前,先问自己几个问题:

  • 读多还是写多?
  • 有没有查询每分钟跑几千上万次?
  • 数据增长速度快不快?
  • 并发事务多不多?
  • 有没有定时批量任务在特定时段压数据库?

每个业务的访问模式天差地别,根本不存在一套配置打天下这种事。

最重要参数:Buffer Pool

如果只让我调一个参数,那必须是 innodb_buffer_pool_size。

Buffer Pool(缓冲池)把热数据页和索引页存在内存里,数据越在内存里,就越少需要去磁盘上折腾。在专机专用的服务器上,通常建议吃到可用内存的 70%~80%。

比如一台 128G 的机器:

innodb_buffer_pool_size = 80G

虚拟化或者容器环境里得适当收着点,别把内存全抢走让系统抖。

原则很简单:内存命中率越高,查询越快。

InnoDB 其他关键配置

Buffer Pool 之外,还有几个参数也值得盯紧:

innodb_buffer_pool_instances = 8
innodb_log_file_size = 2G
innodb_log_buffer_size = 64M
innodb_flush_log_at_trx_commit = 1
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
innodb_flush_method = O_DIRECT

这些数字不是死的,得根据实际情况来:

  • 机器内存多大
  • 磁盘是 SSD 还是机械盘
  • 写压力有多猛
  • 业务高峰期什么时候

照抄别人配置大概率翻车。

Slow Query Log 必须开

不知道哪些查询慢,调优就是盲人摸象。

先把慢查询日志开了:

SET GLOBAL slow_query_log = ON;

设一个合理的阈值:

SET GLOBAL long_query_time = 0.5;

核心业务库通常设得更激进,比如 0.1 秒就算慢。

目的不只是抓那些几秒才返回的查询——很多"还行"的查询每秒跑几万次,累积下来的损耗比偶尔一条10秒的查询可怕多了。

Performance Schema 用起来

Performance Schema(性能架构)在新版 MySQL 里已经相当完善,能看清数据库内部几乎所有指标。

这个查询我经常用:

SELECT *
FROM performance_schema.events_statements_summary_by_digest
ORDER BY avg_timer_wait DESC
LIMIT 20;

跑出来的一般都是这些问题:

  • 资源消耗最高的查询
  • 重复执行次数多的语句
  • CPU 占用异常的家伙
  • 不必要的排序操作
  • 值得优化的其他操作

调配置之前,先搞清楚时间到底花在哪了。

EXPLAIN 是开发标配

一条看着人畜无害的 SQL,可能底层扫了几百万行。养成习惯:每次写复杂查询,先跑个 EXPLAIN 看看执行计划。

重点盯这几个字段:

  • type——访问类型
  • key——用了哪个索引
  • rows——预计扫描行数
  • filtered——过滤比例
  • Extra——额外信息

两个红色警报:

type = ALL

key = NULL

这俩同时出现,基本就是在做全表扫描。小表还好,大表分分钟把你数据库干趴。

复合索引是真香

很多开发喜欢给每列单独建索引,其实往往适得其反。

比如这条查询:

SELECT *
FROM pedidos
WHERE cliente_id = ?
AND status = ?
ORDER BY data;

与其建三个单列索引,不如建一个复合索引:

(cliente_id, status, data)

复合索引能覆盖查询的全部条件,读取次数少一大截,效果立竿见影。

当然,建之前先看 EXPLAIN 验证一下。

索引列上别套函数

这条我见过太多人踩坑了。

别这么写:

WHERE DATE(data_pedido) = CURDATE()

改成这样:

WHERE data_pedido >= CURDATE()
AND data_pedido < CURDATE() + INTERVAL 1 DAY

第一种写法会让索引直接失效,因为函数把索引列包起来了。第二种写法保留了对索引的引用,执行计划能走索引,差距可能是几十倍。

覆盖索引了解一下

Covering Index(覆盖索引)是指索引里已经包含了查询需要的全部列,MySQL 直接在索引树里就能拿到结果,根本不用回表。

比如这个索引:

(cliente_id, status, valor)

如果查询只取这三列,索引数据全都在,磁盘 IO 少一截。这种优化对高频查询效果特别明显。

统计信息要定期更新

优化器选执行计划全靠统计信息。信息过期了,执行计划就可能选错,查询性能直接崩。

定期跑一下:

ANALYZE TABLE pedidos;

MySQL 8 还支持直方图,对数据分布不均匀的场景特别有用:

ANALYZE TABLE clientes
UPDATE HISTOGRAM ON cidade;

尤其那些 WHERE 条件里值分布偏差大的列,比如订单状态、用户等级这类。

Buffer Pool 命中率要监控

这个指标能直接反映内存够不够用:

  • 99% 以上——优秀
  • 98%~99%——需要关注
  • 98% 以下——基本要处理了

命中率越低,意味着越要从磁盘读数据,延迟肯定往上窜。

IO 容量参数要匹配硬件

这两个参数控制 InnoDB 的 IO 吞吐量上限:

innodb_io_capacity
innodb_io_capacity_max

普通 SSD 设为 2000 左右够用,NVMe 盘可以再往上提。

别让数据库的参数拖了硬件的后腿。

高可用架构别忽视

性能不只是快不快,还包括稳不稳。国内现在用得比较多的方案:

  • InnoDB Cluster
  • Group Replication(组复制)
  • MySQL Router
  • ProxySQL

这些不只是用来做负载均衡的,还能实现自动故障切换,数据库挂了应用也能快速恢复。

推荐工具

光靠人工盯肯定不够,这些工具能省不少事:

Percona Monitoring and Management(PMM)

开源的 MySQL 监控方案,功能全,文档详细,小团队用基本够了。

MySQL Enterprise Monitor

企业版内置的监控工具,用 Oracle 原厂方案的话可以考虑。

每次咨询必遇见的那些问题

做了这么多年数据库优化,发现来来回回就那么几类问题:

  • Buffer Pool 配得太小
  • 索引建了一堆重复的
  • 该建索引的地方没建
  • SELECT * 满天飞
  • 查询不带 LIMIT
  • 统计信息从来不更新
  • JOIN 的列没索引
  • 一个查询被重复执行 N 次
  • 生产环境跑着默认配置

大多数情况下,瓶颈真的不在硬件,在于没人管这些基础配置。

总结

MySQL 性能调优不是改几个参数就完事了,是一套系统工程:了解业务、分析 SQL、检查索引、更新统计、持续监控,哪一环都不能少。

做好了,不用加机器也能让查询快好几倍。比起堆硬件,配好数据库、用好数据库,显然更划算。

如果你正在被 MySQL 性能问题折磨,也可以找专业团队来处理。调优这事说起来不难,但真正做的时候细节太多,踩几个坑就浪费不少时间。专业的事交给专业的人,省下的时间用来写业务代码不香吗?