mysql索引为什么可以加速查询_mysql索引原理解析

1次阅读

mysql索引本质是基于b+tree的排序后快速定位结构,非“魔法加速器”;它通过预排序和树形组织实现o(log n)查找,显著优于全表扫描的o(n),但受最左前缀、函数操作等限制易失效。

mysql索引为什么可以加速查询_mysql索引原理解析

索引本质是排序后的快速定位结构

mysql索引不是“魔法加速器”,而是把无序数据预先排好序、再用 B+Tree 组织起来的查找路径。就像查字典不一页页翻,而是先看部首目录、再找页码——索引就是这张“目录”。它不存完整数据(二级索引只存列值 + 主键),但能用 O(log n) 时间定位到目标行,比全表扫描的 O(n) 快得多。10 亿行表查一条记录,B+Tree 通常只需 3–4 次磁盘 I/O;全表扫描则可能读取上万页。

B+Tree 是 InnoDB 的实际执行引擎

你建的每个 INDEXPRIMARY KEYUNIQUE,底层都是 B+Tree。它的关键设计直接决定查询是否高效:

  • 非叶子节点只存键值和指针,不存数据 → 单页能装更多键 → 树更矮 → I/O 更少
  • 所有数据都在叶子节点,且叶子节点用双向链表连成有序链表 → 范围查询(BETWEEN>=)、ORDER BYGROUP BY 都能直接走链表扫描
  • 叶子节点存的是主键值(二级索引)或整行数据(聚簇索引)→ 这就引出“回表”问题:查 select name FROM user WHERE email = 'a@b.com',若只有 email 索引,得先通过索引找到主键,再回聚簇索引查 name 字段

为什么有时加了索引也不快?常见失效场景

索引不是建了就生效。以下操作会让优化器放弃使用索引,退化为全表扫描:

  • 违反最左前缀:对联合索引 INDEX idx_name_age (name, age),查 WHERE age = 25 不走索引;必须带上 name 或者用 WHERE name LIKE '张%' 才可能命中
  • 在索引列上做计算或函数:WHERE YEAR(create_time) = 2025 → 改成 WHERE create_time >= '2025-01-01' AND create_time
  • 隐式类型转换user_idint,却写 WHERE user_id = '123'字符串强制转数字,索引失效
  • 使用 !=NOT INIS NULL(除非是唯一索引且允许空)等,优化器常认为选择率太高,干脆全扫

建索引前必须问自己的三个问题

别一上来就 ALTER table ADD INDEX。先确认:

  • 这列的 SELECT 查询频次是否明显高于 INSERT/UPDATE/delete?建议比值 ≥ 3:1,否则维护索引的写开销(每次 DML 都要更新 B+Tree)可能得不偿失
  • 这个查询是否真的需要返回大量字段?如果是 SELECT id, name FROM user WHERE status = 1,考虑建覆盖索引 INDEX idx_status_name (status, name),避免回表
  • 字段区分度够不够?比如 gender 只有 ‘M’/’F’,建索引几乎无效;而 emailorder_no 就很适合

真正难的不是“怎么建”,而是“该不该建、建在哪、建多宽”——B+Tree 的效率藏在数据分布和查询模式里,不是靠索引数量能解决的。

text=ZqhQzanResources