MySQL 性能调优实战:从慢查询到 B+ 树索引的“排雷”指南
无论你的后端架构设计得多优雅,微服务拆分得多细致,当业务流量真正涌入时,系统的最终瓶颈往往都会卡在关系型数据库上。
在维护包含千万级数据的复杂业务系统(比如多维度的业务监管平台数据看板,或是每天产生大量流转状态的电商订单系统)时,一条写得糟糕的 SQL 语句,就足以让整台 MySQL 服务器的 CPU 瞬间飙升到 100%。今天,结合以往的排雷经验,咱们来聊聊 MySQL 性能调优的那些核心实战技巧。
一、找准病灶:开启慢查询日志
性能优化的第一步永远是监控。不要靠猜去优化 SQL,要用数据说话。
在 MySQL 配置(my.cnf 或 my.ini)中,务必开启慢查询日志:
1 | slow_query_log = 1 |
借助 mysqldumpslow 等工具分析日志,抓出那些频繁执行且耗时极长的“罪魁祸首”。
二、无往不利的 EXPLAIN 指令
拿到慢查询 SQL 后,千万不要急着去建索引。第一反应应该是加上 EXPLAIN 关键字执行一下,看看 MySQL 的执行计划。
重点关注以下几个字段:
- type:连接类型。如果是
ALL(全表扫描),那说明绝对需要优化了。最好能达到ref或range。 - possible_keys 与 key:预测可能会用的索引和实际使用的索引。
- Extra:如果出现了
Using filesort(文件排序)或Using temporary(使用临时表),这通常是极其危险的性能信号。
三、索引失效的“四大天坑”
即便你建了索引,MySQL 也不一定会用。以下是生产环境中极其容易踩坑的索引失效场景:
- 违背最左前缀法则:
如果你建了一个联合索引(a, b, c),你的查询条件必须包含a,才能用上这个索引。如果直接WHERE b = 1 AND c = 2,索引将彻底失效。 - 在索引列上使用函数或运算:
WHERE YEAR(create_time) = 2026这种写法会导致扫描全表。正确的写法应该是范围查询:WHERE create_time >= '2026-01-01' AND create_time < '2027-01-01'。 - 隐式类型转换:
假如order_sn字段是VARCHAR类型,但你查询时写成了WHERE order_sn = 10086(没加引号)。MySQL 会在底层默默套一层CAST()函数把它转成数字,从而导致索引失效。 - 左模糊查询:
LIKE '%keyword'绝对走不了 B+ 树索引。如果非要做全文检索,请老老实实把数据同步到 Elasticsearch 中。
四、深挖底层:为什么极力反感 SELECT *
很多新手为了图省事,总喜欢写 SELECT *。在数据量小的时候看不出区别,但到了千万级大表中,这是大忌。
除了网络传输的带宽开销外,核心原因在于覆盖索引(Covering Index)。
如果你的查询仅仅是 SELECT id, name FROM users WHERE name = 'John',且 name 字段上有索引。MySQL 只需要扫描一遍辅助索引的 B+ 树就能直接拿到 id 和 name,这叫索引覆盖。
一旦你写了 SELECT *,MySQL 在辅助索引树上找不到完整字段,就必须拿着主键 ID 回到主键索引树上再查一遍(这个过程叫回表)。海量数据的回表,带来的磁盘 I/O 开销是毁灭性的。
总结
索引不是建得越多越好。每一颗 B+ 树都需要占据物理磁盘空间,并且在每次 INSERT、UPDATE、DELETE 时都需要付出维护 B+ 树结构的巨大代价。
优秀的后端开发,是在查询效率与写入开销之间,寻找最完美的平衡点。
