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

来源:这里教程网 时间:2026-02-28 20:50:04 作者:

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

MySQL索引不是“魔法加速器”,而是把无序数据预先排好序、再用 B+Tree 组织起来的查找路径。就像查字典不一页页翻,而是先看部首目录、再找页码——索引就是这张“目录”。它不存完整数据(二级索引只存列值 + 主键),但能用

O(log n)
时间定位到目标行,比全表扫描的
O(n)
快得多。10 亿行表查一条记录,B+Tree 通常只需 3–4 次磁盘 I/O;全表扫描则可能读取上万页。

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

你建的每个

INDEX
PRIMARY KEY
UNIQUE
,底层都是 B+Tree。它的关键设计直接决定查询是否高效:

非叶子节点只存键值和指针,不存数据 → 单页能装更多键 → 树更矮 → I/O 更少 所有数据都在叶子节点,且叶子节点用双向链表连成有序链表 → 范围查询(
BETWEEN
>=
)、
ORDER BY
GROUP 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_id
INT
,却写
WHERE user_id = '123'
→ 字符串强制转数字,索引失效
使用
!=
NOT IN
IS 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',建索引几乎无效;而
email
order_no
就很适合

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

相关推荐