SQL 性能调优的系统化思维

SQL性能调优需同步验证查询执行路径、数据分布、统计信息和存储引擎行为四层面;EXPLAIN关键看rows(估算扫描行数)、key(实际索引名,NULL可能因覆盖索引)、Extra中Using filesort/Using temporary;复合索引须按等值→范围→排序顺序设计;JOIN字段禁用函数或类型转换;统计信息不准须手动ANALYZE TABLE。

SQL 性能调优不是靠猜,也不是堆索引就能解决的;它需要从查询执行路径、数据分布、统计信息和存储引擎行为四个层面同步验证。跳过任一环节,都可能把 WHERE 条件优化了,却让 JOIN 变*表扫描。

看懂 EXPLAIN 输出里真正关键的三列

EXPLAIN 不是只看 type 是不是 refrange,重点在三处:

  • rows:MySQL 估算的扫描行数,如果远大于实际匹配行数(比如估算 10 万,实际只返回 5 行),说明统计信息过期或索引选择错误
  • key:实际用到的索引名,为空不等于没走索引——可能是用了覆盖索引但 key 显示为 NULL(如 SELE

    CT id FROM t WHERE status=1
    走了 status 索引且该索引包含 id
  • Extra 中的 Using filesortUsing temporary:意味着排序或分组没走索引,即使 WHERE 很快,整体也会慢

复合索引字段顺序不能只按 WHERE 出现顺序排

字段顺序必须匹配“等值条件 → 最左前缀 → 范围/排序字段”这个执行逻辑:

  • 等值条件(=IN)字段放最左,且优先放区分度高的(如 user_idstatus 更适合放前面)
  • 范围条件(>BETWEEN)只能放最后一位,它后面的字段无法被索引下推
  • ORDER BY 字段如果要避免 Using filesort,必须紧接在等值字段之后,且方向一致(如 INDEX(a,b,c) 支持 WHERE a=1 ORDER BY b,c,但不支持 ORDER BY c,b

别在 JOIN 字段上加函数或类型转换

哪怕只是 CAST(user_id AS CHAR)LOWER(email),都会让关联字段失效索引:

  • MySQL 8.0+ 对部分函数(如 JSON_EXTRACT)支持函数索引,但普通字符串函数不行
  • 常见陷阱:ON a.id = b.user_id + 0(隐式转数字)、ON a.code = UPPER(b.code)(大小写不一致)
  • 修复方式:统一字段类型、提前计算好值存入冗余列、或用生成列(GENERATED COLUMN)加索引

统计信息不准会直接误导优化器选错执行计划

尤其在大表批量导入/删除后,ANALYZE TABLE 不是可选项,是必做动作:

  • 默认采样率低(innodb_stats_sample_pages=20),小样本遇上倾斜数据(如 95% 的 status=1,5% 的 status=2),优化器会误判 status=2 也很快
  • 手动更新:运行 ANALYZE TABLE orders;,或调高采样页数(SET GLOBAL innodb_stats_sample_pages = 100;
  • 注意:InnoDB 的统计信息是后台异步更新的,SHOW INDEX 里的 Cardinality 值可能滞后几分钟

最常被忽略的是:同一个 SQL 在不同数据量级下,最优索引可能完全相反;没有银弹索引,只有匹配当前数据分布和查询模式的索引。