无论你的后端架构设计得多优雅,微服务拆分得多细致,当业务流量真正涌入时,系统的最终瓶颈往往都会卡在关系型数据库上。

在维护包含千万级数据的复杂业务系统(比如多维度的业务监管平台数据看板,或是每天产生大量流转状态的电商订单系统)时,一条写得糟糕的 SQL 语句,就足以让整台 MySQL 服务器的 CPU 瞬间飙升到 100%。今天,结合以往的排雷经验,咱们来聊聊 MySQL 性能调优的那些核心实战技巧。

一、找准病灶:开启慢查询日志

性能优化的第一步永远是监控。不要靠猜去优化 SQL,要用数据说话。
在 MySQL 配置(my.cnfmy.ini)中,务必开启慢查询日志:

1
2
3
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 1 # 记录执行时间超过 1 秒的 SQL

借助 mysqldumpslow 等工具分析日志,抓出那些频繁执行且耗时极长的“罪魁祸首”。

二、无往不利的 EXPLAIN 指令

拿到慢查询 SQL 后,千万不要急着去建索引。第一反应应该是加上 EXPLAIN 关键字执行一下,看看 MySQL 的执行计划。

重点关注以下几个字段:

  • type:连接类型。如果是 ALL(全表扫描),那说明绝对需要优化了。最好能达到 refrange
  • possible_keyskey:预测可能会用的索引和实际使用的索引。
  • Extra:如果出现了 Using filesort(文件排序)或 Using temporary(使用临时表),这通常是极其危险的性能信号。

三、索引失效的“四大天坑”

即便你建了索引,MySQL 也不一定会用。以下是生产环境中极其容易踩坑的索引失效场景:

  1. 违背最左前缀法则
    如果你建了一个联合索引 (a, b, c),你的查询条件必须包含 a,才能用上这个索引。如果直接 WHERE b = 1 AND c = 2,索引将彻底失效。
  2. 在索引列上使用函数或运算
    WHERE YEAR(create_time) = 2026 这种写法会导致扫描全表。正确的写法应该是范围查询:WHERE create_time >= '2026-01-01' AND create_time < '2027-01-01'
  3. 隐式类型转换
    假如 order_sn 字段是 VARCHAR 类型,但你查询时写成了 WHERE order_sn = 10086(没加引号)。MySQL 会在底层默默套一层 CAST() 函数把它转成数字,从而导致索引失效。
  4. 左模糊查询
    LIKE '%keyword' 绝对走不了 B+ 树索引。如果非要做全文检索,请老老实实把数据同步到 Elasticsearch 中。

四、深挖底层:为什么极力反感 SELECT *

很多新手为了图省事,总喜欢写 SELECT *。在数据量小的时候看不出区别,但到了千万级大表中,这是大忌。

除了网络传输的带宽开销外,核心原因在于覆盖索引(Covering Index)
如果你的查询仅仅是 SELECT id, name FROM users WHERE name = 'John',且 name 字段上有索引。MySQL 只需要扫描一遍辅助索引的 B+ 树就能直接拿到 idname,这叫索引覆盖。
一旦你写了 SELECT *,MySQL 在辅助索引树上找不到完整字段,就必须拿着主键 ID 回到主键索引树上再查一遍(这个过程叫回表)。海量数据的回表,带来的磁盘 I/O 开销是毁灭性的。

总结

索引不是建得越多越好。每一颗 B+ 树都需要占据物理磁盘空间,并且在每次 INSERTUPDATEDELETE 时都需要付出维护 B+ 树结构的巨大代价。
优秀的后端开发,是在查询效率与写入开销之间,寻找最完美的平衡点。