联合索引底层是怎么样的?

1 个回答

  • 慕骨天尊
    优先考虑那些先考虑你的人
    下面以 InnoDB 为例,讲清“联合索引(复合索引)”在底层是怎么存、怎么比、怎么用。 # 1)数据结构与存储 * 索引结构:B+Tree。内部节点只存“键元组的前缀”和子页指针;叶子节点按键元组有序排列,串成双向链表,便于区间扫描。 * 聚簇与二级: * 聚簇索引(PRIMARY KEY)叶子存整行记录。 * 二级索引叶子存:**(联合索引键元组) + 主键值**。主键作为“隐含后缀”保证二级索引键唯一,也用于回表。 * 页:默认 16KB,一页内是多条有序记录,带页目录;插入可能触发页分裂,删除可能合并。 # 2)“键元组”的比较规则(联合索引怎么排序) * 记索引定义:`INDEX idx(a, b, c)`,每条索引记录的键就是三元组 `(a, b, c)`,比较采用**字典序**:先比 `a`,相等再比 `b`,再比 `c`。 * 字符列受字符集/排序规则影响;`NULL`可参与比较(通常当作最小)。 * MySQL 8.0 支持 per-column `ASC/DESC`,底层仍是同一棵 B+Tree,只是比较方向不同。 * 可定义“列前缀索引”:如 `INDEX idx(name(20), city(10))`,每列的前缀长度独立生效。 # 3)查找与范围扫描是怎么走的 * 精确查找:用已知前缀在树上二分下探,最终定位到叶子上的第一个匹配位置。 * 范围扫描:从定位到的第一条开始,沿叶子链表顺序扫描,直到区间上界。 * 左前缀原则(Leftmost Prefix Rule)与“范围停止”: * 只有**从最左列开始的连续条件**才能用于“定位范围”。 例:`a = ? AND b = ?` 可用到 `(a,b)`;`b = ?` 单独用不到这个索引(可能触发 skip-scan,见下)。 * 一旦在第 k 列出现**范围条件**(`> < BETWEEN LIKE 'xx%'`),就**不能再用第 k+1 列继续缩小索引范围**;第 k+1 列只能用于“过滤”(ICP)或回表后过滤。 例:`a = ? AND b BETWEEN ? AND ? AND c = ?`,`c`通常不能参与索引范围界定。 * Index Condition Pushdown(ICP):不再缩小“索引范围”的条件,若可在索引里判断,会被下推到存储引擎层减少回表。 # 4)回表与覆盖 * 覆盖索引:查询所需列全部在联合索引里,叶子记录即可返回,`Extra: Using index`,无回表。 * 非覆盖:需要去聚簇索引再拿整行。二级索引记录里的主键值就是回表的“指针”。 # 5)空间与 key\_len * `EXPLAIN key_len`≈真实参与比较的索引前缀字节数之和:每列的定长/变长开销、`NULL` 标志位都会计入。 变长列(VARCHAR)会多长度字节;可空列会多 NULL 位。理解 `key_len` 能看出复合索引用到了哪些列、是否只用到了前缀。 * 二级索引额外握着主键值;主键越长,所有二级索引越大、写入越慢。 # 6)维护行为 * 插入/更新:保持有序,可能页分裂与树高变化;高写入下随机主键比顺序主键更易产生碎片。 * 删除:标记 + 按需合并;长时间剧烈写入后可考虑重建索引来整理碎片。 # 7)优化器的几个特殊点 * Skip Scan(8.0 可用):当左列基数很小且未给条件时,优化器可能尝试“假装枚举几种左值”后利用右侧列,代价模型决定是否采用;并不总是触发。 * 排序/分组下推:若 `ORDER BY/GROUP BY` 的列序与联合索引顺序、方向一致且等值前缀满足,可“顺序读即有序”,避免 `Using filesort/temporary`。 # 8)把规则落到查询上(`INDEX(a,b,c)`) 可用到索引“定位范围”的典型组合: * `a = ?` * `a = ? AND b = ?` * `a = ? AND b = ? AND c = ?` * `a = ? AND b BETWEEN ? AND ?`(到 b 为止,c 只做过滤/ICP) * `a BETWEEN ? AND ?`(到 a 为止) * `a = ? AND b LIKE 'xxx%'`(到 b 为止,前缀匹配) 不太行(或依赖 skip-scan): * 只有 `b = ?`、`c = ?`、或 `b BETWEEN ? AND ?` * `a LIKE '%xx'`(左模糊无法定位) # 9)列顺序的经验准则(设计联合索引时) * 以**等值匹配最多**的列在前,其次是范围列;把“会截断范围”的列尽量放靠后。 * 若常需要**排序或分组**,让索引顺序与 `ORDER BY/GROUP BY` 一致,且前缀条件为等值,这样能“边读边有序”。 * 选择性(区分度)高的列优先,但要与实际查询模式综合权衡。 * 控制主键长度,避免拖大所有二级索引;需要覆盖就把常查列放进索引,但注意写放大。
    发布于 2025-08-13 22:02 •IP属地 江苏
    举报
    评论
    收藏