覆盖 B+Tree · Hash · Full-Text · R-Tree · 物理结构 · 使用策略 · 最左前缀 · 执行计划
| 类型 | 关键字 | 特点 |
|---|---|---|
| 主键索引 | PRIMARY KEY | 唯一+非空,InnoDB聚簇 |
| 唯一索引 | UNIQUE | 值唯一,允许NULL |
| 普通索引 | INDEX / KEY | 无约束,最常用 |
| 联合索引 | INDEX(a,b,c) | 多列,最左前缀原则 |
| 全文索引 | FULLTEXT | 文本搜索,倒排表 |
| 空间索引 | SPATIAL | 地理空间,R-Tree |
| 类型 | 引擎 | 说明 |
|---|---|---|
| 聚簇索引 | InnoDB | 数据与索引在同一 B+Tree,叶节点存完整行 |
| 非聚簇索引 | MyISAM | 索引文件(.MYI)与数据文件(.MYD)分离,叶节点存地址 |
| 辅助索引 | InnoDB | 叶节点存主键值,回表查询完整行 |
| 覆盖索引 | 均支持 | 查询列全在索引中,无需回表 |
| 特性 | B Tree | B+ Tree |
|---|---|---|
| 数据位置 | 所有节点 | 仅叶节点 |
| 叶节点链表 | ❌ | ✅ 双向链表 |
| 范围查询 | 需中序遍历 | 链表顺序扫 |
| 内节点空间 | 存数据,占空间 | 只存键,更紧凑 |
| 查询稳定性 | 不稳定 | 稳定(必到叶) |
| MySQL使用 | ❌ | ✅ 全面使用 |
prev/next 指针相连,执行范围查询(BETWEEN、>、<)时,找到起始叶节点后,直接顺着链表扫描,无需再回到根节点,极大提升范围扫描效率。
row_id(无主键时)、trx_id(事务ID,MVCC用)、roll_pointer(回滚指针,指向 undo log)
SELECT * FROM t WHERE name='Bob'name='Bob' → id=3SELECT id, name FROM t WHERE name='Bob' — name索引含id,直接返回,EXPLAIN中 Extra 显示 Using index
.MYD(MYData)— 存实际行数据.MYI(MYIndex)— 存 B+Tree 索引结构key → hash(key) → 取桶 → 遍历链表比较 → 返回 row_ptr
| 维度 | Hash 索引 | B+Tree 索引 |
|---|---|---|
| 等值查询 | O(1) | O(log n) |
| 范围查询 | 不支持 | ✅ 高效 |
| 排序 | 不支持 | ✅ 有序 |
| 前缀查询 | 不支持 | ✅ 支持 |
| 联合索引 | 不支持部分列 | ✅ 最左前缀 |
| 适用引擎 | Memory | InnoDB/MyISAM |
innodb_adaptive_hash_index=OFF
MATCH(col) AGAINST('keyword' IN BOOLEAN MODE)NATURAL LANGUAGE MODE(自然语言)和 BOOLEAN MODE(布尔操作 +/-/*)
| 概念 | 说明 |
|---|---|
| MBR | Minimum Bounding Rectangle 最小外包矩形,每个节点存储其子节点的MBR |
| 插入 | 选择MBR面积增量最小的分支插入,必要时分裂 |
| 查询 | 从根节点递归检查MBR是否与查询区域相交,剪枝不相交分支 |
| 分裂 | 节点溢出时,使用二次分裂(Quadratic)或线性分裂算法 |
CREATE SPATIAL INDEX idx_loc ON shops(location);SELECT * FROM shops WHERE ST_Contains(region, location);GEOMETRY / POINT / POLYGON 等空间类型
CREATE TABLE t (
id INT PRIMARY KEY,
name VARCHAR(50)
);
CREATE UNIQUE INDEX
idx_email
ON users(email);
CREATE INDEX idx_age
ON users(age);
-- 或建表时
KEY idx_age (age)
VARCHAR/TEXT/BLOB 等长文本列,只取前 N 个字符建索引,节省空间。CREATE INDEX idx_name
ON users(name(10));
-- 只索引 name 的前10字符
SELECT
COUNT(DISTINCT LEFT(name, 5)) / COUNT(*),
COUNT(DISTINCT LEFT(name, 8)) / COUNT(*),
COUNT(DISTINCT LEFT(name, 10)) / COUNT(*)
FROM users;
-- 选择性接近完整列时即可
| 查询条件 | 是否使用索引 | 使用的索引列 | 说明 |
|---|---|---|---|
WHERE a=1 | ✅ 全用 | a | 前缀匹配 |
WHERE a=1 AND b=2 | ✅ 全用 | a, b | 连续前缀 |
WHERE a=1 AND b=2 AND c=3 | ✅ 全用 | a, b, c | 全列匹配 |
WHERE a=1 AND c=3 | ⚠️ 部分 | 仅 a | b断了,c无法使用 |
WHERE b=2 | ❌ 不用 | 无 | 跳过最左列a |
WHERE b=2 AND c=3 | ❌ 不用 | 无 | 缺少最左列 |
WHERE a>1 AND b=2 | ⚠️ 部分 | 仅 a | a范围查询后b失效 |
WHERE a=1 AND b>2 AND c=3 | ⚠️ 部分 | a, b | b范围查询后c失效 |
ORDER BY a, b | ✅ 可排序 | a, b | 无需filesort |
WHERE a=1 ORDER BY b | ✅ 可排序 | a, b | a等值+b排序 |
EXPLAIN 中体现为 Using index for skip scan。
| 特性 | B+Tree | Hash | Full-Text | R-Tree |
|---|---|---|---|---|
| 底层结构 | 多路平衡树 | 哈希表+链表 | 倒排索引 | 矩形树 |
| 等值查询 | O(log n) | O(1) | 不适用 | 不适用 |
| 范围查询 | ✅ 高效 | ❌ 不支持 | ❌ 不支持 | ❌ 不支持 |
| 排序/ORDER BY | ✅ 天然有序 | ❌ | ❌ | ❌ |
| 模糊搜索 | 仅前缀 LIKE 'x%' | ❌ | ✅ 全文分词 | ❌ |
| 空间查询 | ❌ | ❌ | ❌ | ✅ MBR相交 |
| 联合索引 | ✅ 最左前缀 | 整体hash | ❌ | ❌ |
| 覆盖索引 | ✅ | ❌ | ❌ | ❌ |
| 支持引擎 | InnoDB/MyISAM | Memory/InnoDB(AHI) | InnoDB/MyISAM | InnoDB/MyISAM |
| 存储开销 | 中等 | 低 | 大 | 中等 |
| 字段 | 含义 | 重点关注值 |
|---|---|---|
type |
访问类型(最重要) |
system→const→eq_ref→ref→range→index→ALL(全表扫) 从左到右性能递减,至少达到 range 级别 |
key |
实际使用的索引名 | NULL 表示未使用索引 |
key_len |
使用的索引字节数 | 越大代表使用了更多索引列 |
rows |
预估扫描行数 | 越小越好 |
Extra |
附加信息 |
Using index 覆盖索引 Using where 回表过滤 Using filesort 内存排序(需优化) Using temporary 临时表(需优化) Using index condition ICP下推 |
possible_keys |
可能使用的索引 | 与 key 不同时,说明优化器放弃了某些索引 |
| 失效原因 | 示例 |
|---|---|
| 列上做函数运算 | WHERE YEAR(create_time)=2024 |
| 列上做隐式类型转换 | WHERE phone=13800138000(phone是字符串) |
| LIKE 前缀通配符 | WHERE name LIKE '%abc' |
| OR 有非索引列 | WHERE id=1 OR addr='北京'(addr无索引) |
| NOT IN / NOT EXISTS | 全表扫描替代 |
| 联合索引跳过最左列 | WHERE b=1 AND c=2(缺a) |
| 范围查询后的列 | WHERE a>1 AND b=2(b失效) |
| NULL 值判断 | WHERE col IS NULL(有时可用,看情况) |
| 使用 != / <> | 全表扫更快 |
SELECT id, name WHERE name=? 优于 SELECT *,Extra 显示 Using index。
INDEX(status, age, create_time) 适合 WHERE status=1 AND age>18
EXPLAIN Extra 显示 Using MRR
-- 启用/禁用 MRR
SET optimizer_switch =
'mrr=on,mrr_cost_based=off';
-- read_rnd_buffer_size 控制排序缓冲区
SET read_rnd_buffer_size = 262144;