索引本质是排序后的快速定位结构
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',若只有
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',建索引几乎无效;而
order_no就很适合
真正难的不是“怎么建”,而是“该不该建、建在哪、建多宽”——B+Tree 的效率藏在数据分布和查询模式里,不是靠堆索引数量能解决的。
