
最近折腾 MySQL 8.4,在几套生产环境里踩了几个坑,这篇把问题说清楚,不整虚的。
MySQL 依然是国内最流行的数据库之一,从电商、ERP到各种后台系统,到处都在跑。但说实话,我见过太多服务器配置拉满却还是慢得像蜗牛的情况——问题往往不在硬件,在于那些没人管的配置和SQL。
MySQL 8.4 LTS 确实带来了不少改进:优化器更聪明了、统计信息管理更完善、对JSON的支持也顺手多了。但现实是,绝大多数性能问题跟版本没关系。
改配置、调索引、加监控——这篇把实操经验都摊开讲。
CPU 高不代表要加内存,磁盘飘红也不一定是 IO 的锅。我见过好几台高配机器资源利用率低得可怜,原因就是几个慢查询把整个库拖下水。
下面这些症状出现超过两个,基本可以确定有优化空间:
踩中三条以上的话,这篇文章你算是来对了。
很多人一上来就改 my.cnf,改完发现毛用没有——因为根本不了解业务怎么用数据库。
调参之前,先问自己几个问题:
每个业务的访问模式天差地别,根本不存在一套配置打天下这种事。
如果只让我调一个参数,那必须是 innodb_buffer_pool_size。
Buffer Pool(缓冲池)把热数据页和索引页存在内存里,数据越在内存里,就越少需要去磁盘上折腾。在专机专用的服务器上,通常建议吃到可用内存的 70%~80%。
比如一台 128G 的机器:
innodb_buffer_pool_size = 80G 虚拟化或者容器环境里得适当收着点,别把内存全抢走让系统抖。
原则很简单:内存命中率越高,查询越快。
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 这些数字不是死的,得根据实际情况来:
照抄别人配置大概率翻车。
不知道哪些查询慢,调优就是盲人摸象。
先把慢查询日志开了:
SET GLOBAL slow_query_log = ON; 设一个合理的阈值:
SET GLOBAL long_query_time = 0.5; 核心业务库通常设得更激进,比如 0.1 秒就算慢。
目的不只是抓那些几秒才返回的查询——很多"还行"的查询每秒跑几万次,累积下来的损耗比偶尔一条10秒的查询可怕多了。
Performance Schema(性能架构)在新版 MySQL 里已经相当完善,能看清数据库内部几乎所有指标。
这个查询我经常用:
SELECT *
FROM performance_schema.events_statements_summary_by_digest
ORDER BY avg_timer_wait DESC
LIMIT 20; 跑出来的一般都是这些问题:
调配置之前,先搞清楚时间到底花在哪了。
一条看着人畜无害的 SQL,可能底层扫了几百万行。养成习惯:每次写复杂查询,先跑个 EXPLAIN 看看执行计划。
重点盯这几个字段:
两个红色警报:
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 条件里值分布偏差大的列,比如订单状态、用户等级这类。
这个指标能直接反映内存够不够用:
命中率越低,意味着越要从磁盘读数据,延迟肯定往上窜。
这两个参数控制 InnoDB 的 IO 吞吐量上限:
innodb_io_capacity
innodb_io_capacity_max 普通 SSD 设为 2000 左右够用,NVMe 盘可以再往上提。
别让数据库的参数拖了硬件的后腿。
性能不只是快不快,还包括稳不稳。国内现在用得比较多的方案:
这些不只是用来做负载均衡的,还能实现自动故障切换,数据库挂了应用也能快速恢复。
光靠人工盯肯定不够,这些工具能省不少事:
Percona Monitoring and Management(PMM)
开源的 MySQL 监控方案,功能全,文档详细,小团队用基本够了。
MySQL Enterprise Monitor
企业版内置的监控工具,用 Oracle 原厂方案的话可以考虑。
做了这么多年数据库优化,发现来来回回就那么几类问题:
大多数情况下,瓶颈真的不在硬件,在于没人管这些基础配置。
MySQL 性能调优不是改几个参数就完事了,是一套系统工程:了解业务、分析 SQL、检查索引、更新统计、持续监控,哪一环都不能少。
做好了,不用加机器也能让查询快好几倍。比起堆硬件,配好数据库、用好数据库,显然更划算。
如果你正在被 MySQL 性能问题折磨,也可以找专业团队来处理。调优这事说起来不难,但真正做的时候细节太多,踩几个坑就浪费不少时间。专业的事交给专业的人,省下的时间用来写业务代码不香吗?