MySQL调优别急着改参数:先看慢查询日志,再谈索引与内存
数据库一慢,很多人的第一反应是翻配置文件,把 innodb_buffer_pool_size 从默认值往上调,调完发现没快多少。问题多半不在参数上。MySQL 调优这件事,顺序错了会白忙很久:先定位是哪条 SQL 在拖后腿,再判断是写法问题还是资源问题,最后才轮到改配置和换硬件。
我一般会先让慢查询日志跑够一天的真实流量,而不是拿几条测试语句反复试。真实业务里的慢,和测试环境里的慢,经常不是一回事。
先打开慢查询日志,把最耗时的几条SQL揪出来
慢查询日志默认是关的,得手动开。在 my.cnf 里加上 slow_query_log = 1,再设 long_query_time = 1,单位是秒。跑满一个业务周期之后,用 mysqldumpslow 或者 pt-query-digest 汇总,重点看两个指标:执行次数多且单次耗时高的,和单次耗时特别夸张的。
这两类要分开处理。前者往往是缺索引,后者可能是全表扫描或者锁等待。混在一起看容易抓错重点。
拿到具体 SQL 之后,前面加 EXPLAIN 看执行计划。重点看 type 这一列:出现 ALL 说明全表扫描,出现 index 说明扫了整个索引树,理想状态是 ref 或 range。再看 rows 估算扫描行数,如果这个数字接近表的总行数,索引基本等于没建。
有个常见的坑:WHERE 条件里对字段做了函数运算或者隐式类型转换,索引直接失效。比如 WHERE DATE(create_time) = '2026-01-01' 这种写法,改成 WHERE create_time >= '2026-01-01' AND create_time < '2026-01-02' 才能用上索引。
索引不是越多越好,联合索引的顺序决定它有没有用
单列索引建一堆,不如按查询模式建联合索引。联合索引遵循最左前缀原则:索引 (a, b, c) 能命中 a、a+b、a+b+c 这三种查询,但单独查 b 或者查 b+c 就用不上。
所以建联合索引之前,先看 WHERE 条件里哪些字段经常一起出现,把区分度最高的放最左边。区分度就是字段不同值的比例,比如用户 ID 的区分度接近 1,性别只有两三个值,区分度极低,放左边没意义。
- 等值查询的字段放前面,范围查询的字段放后面,因为范围查询之后的字段用不上索引
- ORDER BY 和 GROUP BY 用到的字段,如果和 WHERE 条件能组成同一个联合索引,排序开销可以省掉
- 索引会拖慢写入,表上超过五六个索引就要警惕,每次 INSERT 都要维护所有索引
用 EXPLAIN 看 key 这一列,确认实际用上了哪个索引。有时候建了索引但优化器不选,可能是统计信息过期,ANALYZE TABLE 一下再看。
覆盖索引是另一个省事的方向:如果查询需要的字段全在索引里,MySQL 不用回表查主键,速度差别很明显。EXPLAIN 的 Extra 列出现 Using index 就说明命中了覆盖索引。
参数和硬件什么时候才该动
SQL 和索引都收拾干净了,还慢,才轮到看资源。
innodb_buffer_pool_size 是最值得调的一个。它缓存的是数据和索引页,设成物理内存的 50% 到 70% 是常见做法。设太小,读操作频繁走磁盘;设太大,操作系统和其他进程没内存用,反而会触发 swap。
连接数也别放任。max_connections 设成几千看着豪气,实际上每个连接都要占内存和线程资源,连接一多上下文切换开销就上来了。按我的经验,先看业务峰值实际并发是多少,留出 20% 余量就够,配合连接池使用,别让应用层随便开连接。
如果 EXPLAIN 显示没有全表扫描、索引也命中了,但查询还是慢,大概率是磁盘 IO 到顶了。这时候调参数没用,加内存也没用,得看存储介质。机械盘换 SSD 的提升是数量级的,尤其是随机读写密集的场景。
机器本身扛不住的话,换一台磁盘性能更好的服务器比继续调参划算。像 马来西亚大带宽服务器 I 这类配置,E5-2683v4 双路加 64G 内存,跑中小型 MySQL 实例够用,磁盘换成 SSD 之后随机 IO 会明显改善。选机器的时候盯住磁盘类型和内存容量这两项,CPU 核心数对数据库来说反而没那么关键。
结论很直接:先花半天看慢查询日志,把 top 3 的 SQL 用 EXPLAIN 过一遍,该加索引加索引,该改写改写。这一步能解决大多数性能问题。剩下的才考虑调 innodb_buffer_pool_size 和换 SSD。如果表数据量已经过亿、单表查询再优化也压不下去,那就该考虑分库分表,那是另一个话题了。