SQL 优化(覆盖索引 / 回表 / 下推 / join 原理)

1️⃣ 覆盖索引(Covering Index)

核心理念:

“如果索引里有我需要的所有列,就不用回表读数据页。”

例子:

SELECT id, name FROM user WHERE age = 20;

假设你有索引 (age, id, name),那么:

  • 查询可以直接在 B+Tree 叶子节点找到 id、name
  • 不用访问聚簇索引(回表)
  • I/O 极少,查询飞快

覆盖索引优势:减少回表 → 减少磁盘随机 I/O → 减少 Buffer Pool 命中压力

注意

  • 覆盖列过多,索引膨胀 → 写入慢
  • 不包含查询列 → 回表不可避免

2️⃣ 回表(Index Lookup / Clustered Index Lookup)

核心理念:

二级索引叶子只存主键,查到结果必须回聚簇索引(主键索引)拿完整数据。

流程:

  1. 查二级索引 (age) → 找到 id=100
  2. 再去聚簇索引找 id=100 → 拿整行数据

💥 性能杀手

  • 单行回表 OK
  • 范围查询/大批量回表 → 大量随机 I/O → 慢

优化方式

  • 使用覆盖索引
  • 调整索引列顺序,让二级索引包含查询列
  • 减少 SELECT *

3️⃣ 下推(Predicate Pushdown / Index Condition Pushdown)

核心理念:

尽量让索引过滤掉更多行,而不是先拿到行再在内存过滤。

传统:

SELECT * FROM user WHERE age = 20 AND name LIKE 'Tom%';
  • 先用索引 age 找到很多行
  • 再回表读整行 → 再在内存中过滤 name

下推优化:

  • InnoDB 5.6+ 支持 Index Condition Pushdown (ICP)
  • 在索引叶子节点直接判断 name LIKE 'Tom%'
  • 减少回表次数

💡 画面感:

你像在 B+Tree 叶子节点做“二次过滤”,避免不必要的随机 I/O。


4️⃣ Join 原理(Nested Loop + Hash + Merge)

MySQL 常用 join 策略:

1️⃣ 嵌套循环(Nested Loop Join,默认 InnoDB 方式)

SELECT * 
FROM A JOIN B ON A.id = B.a_id;

流程:

  1. 外表 A 一行一行读
  2. 用 B 的索引查匹配行(Index Lookup)
  3. 返回结果

✅ 优点:简单、索引优化效果好
❌ 缺点:大表 join 大表 → 随机 I/O 多 → 慢


2️⃣ Batched Key Access / Block Nested Loop

优化版 Nested Loop:

  • 批量从 B 取索引 → 减少随机 I/O
  • InnoDB 5.6+ 自带

3️⃣ Hash Join(MySQL 8.0+ 支持部分场景)

  • 小表 build hash table
  • 大表 probe hash → 快速匹配
  • 减少嵌套循环次数

4️⃣ Merge Join(排序合并)

  • 两个表先排序
  • 顺序扫描匹配 → 减少随机 I/O
  • MySQL InnoDB 默认不常用,需要 optimizer hint

5️⃣ 工程师视角总结优化思路

优化手段 作用 场景
覆盖索引 减少回表 SELECT 列少,二级索引列可覆盖
回表优化 尽量少回表 避免 SELECT * / 调整索引列
下推(ICP) 索引页过滤 范围查询 + 二次条件
Join 顺序优化 外表小 → 内表大 避免大表嵌套循环
合理索引 包含 join/on/filter 字段 减少随机 I/O

6️⃣ 工程实践技巧(你实际能用的)

  1. EXPLAIN + Buffer Pool

    • 看 join 顺序、索引使用、回表次数
    • 判断覆盖索引命中情况
  2. **少用 SELECT ***

    • 回表成本高,覆盖索引才真正快
  3. 索引列顺序

    • WHERE 列前置 → 提高索引命中
    • join/on 字段在索引里 → 减少全表扫描
  4. 小表驱动大表

    • Nested Loop join 顺序调整 → 减少随机 I/O
posted @ 2025-12-06 14:57  中登程序猿  阅读(0)  评论(0)    收藏  举报