在MySQL中,使用前缀索引(Prefix Index)是一种优化查询性能的策略,特别是在处理长字符串字段时。通过仅对字段的一部分创建索引,可以减少索引的大小,从而加快查询速度并节省磁盘空间。

前缀索引:对字符串类型字段,不取完整字段,只截取前若干个字符建立索引。适用场景:CHAR、VARCHAR、TEXT 等长文本字段,减少索引体积、提升索引效率。
-- 格式:CREATE INDEX 索引名 ON 表名(字段(截取长度));CREATE INDEX 索引名 ON 表名(字段名(N));
CREATE TABLE student ( sid INT PRIMARY KEY AUTO_INCREMENT, sname VARCHAR(50), address VARCHAR(200) -- 地址字段较长) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 截取 sname 前3位字符创建索引CREATE INDEX idx_name_pre3 ON student(sname(3));
CREATE INDEX idx_addr_pre10 ON student(address(10));
SHOW INDEX FROM student;
结果中会显示索引长度为设定的截取位数。
前缀匹配查询(右模糊),和索引截取规则一致:
-- 正常走前缀索引:从开头匹配SELECT * FROM student WHERE sname LIKE '张%';SELECT * FROM student WHERE address LIKE '北京市%';
SELECT * FROM student WHERE sname LIKE '%三'; -- 失效SELECT * FROM student WHERE sname LIKE '%李%'; -- 失效
目标:区分度尽可能高,接近完整字段的查询效果。
区分度 = 不同前缀值数量 / 总数据行数区分度越接近 1,效果越好。
-- 1. 统计整列不重复值数量SELECT COUNT(DISTINCT sname) FROM student;-- 2. 依次测试前1、2、3...位字符的不重复数量SELECT COUNT(DISTINCT LEFT(sname,1)) FROM student;SELECT COUNT(DISTINCT LEFT(sname,2)) FROM student;SELECT COUNT(DISTINCT LEFT(sname,3)) FROM student;
选取区分度趋于稳定的最小长度,节约空间又保证效率。
无法使用覆盖索引因为索引只存前缀字符,无法拿到完整字段值,查询一定会回表。
排序、分组无法使用该前缀索引
-- 无法走前缀索引排序SELECT * FROM student ORDER BY sname;
左模糊查询直接失效
-- 删除前缀索引DROP INDEX idx_name_pre3 ON student;-- 用 ALTER 方式创建前缀索引ALTER TABLE student ADD INDEX idx_addr_pre5(address(5));
-- 联合索引 + 前缀CREATE INDEX idx_class_name ON student(classid, sname(3));
给出例子,如何依据前缀创建b树
结合 MySQL 前缀索引 + B + 树(InnoDB 默认索引结构),用实例讲清:截取字段前缀后,数据如何存入 B + 树、查询过程、生效 / 失效场景,附可视化结构。
CREATE TABLE student ( sid INT PRIMARY KEY AUTO_INCREMENT, sname VARCHAR(50) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- 插入测试数据INSERT INTO student(sname) VALUES ('张三'),('张三丰'),('张小花'),('李四'),('李磊'),('王五');截取 sname 前 2 个字符 建立前缀索引:
-- 对姓名字段前2位字符创建前缀索引CREATE INDEX idx_sname_pre2 ON student(sname(2));
说明:InnoDB 所有索引底层都是 B + 树,该前缀索引同样基于 B + 树 存储。
截取规则:只取字符串开头前 2 个字符
表格
| 原始姓名 | 截取前缀 (前 2 位) | 主键 sid |
|---|---|---|
| 张三 | 张三 | 1 |
| 张三丰 | 张三 | 2 |
| 张小花 | 张小花(取前 2 位:张小花→张小) | 3 |
| 李四 | 李四 | 4 |
| 李磊 | 李磊 | 5 |
| 王五 | 王五 | 6 |
按字符编码升序排序后,索引排序序列:张三(1)、张三(2)、张小(3)、李四(4)、李磊(5)、王五(6)
InnoDB 二级索引通用格式:叶子节点 = 截取的前缀字符 + 主键值非叶子节点 = 前缀分界字符(仅用于引路,不存完整数据)
【根节点(非叶子)】 张三 李四 ↙ ↘【叶子组1】 【叶子组2】(张三,1) (张三,2) (张小,3) (李四,4) (李磊,5) (王五,6)
(前缀字符, 主键sid),是索引真实数据;所有叶子节点通过双向链表串联,保持有序。("张三",1) 、("张三",2) 、("张小",3) 、("李四",4) 、("李磊",5) 、("王五",6)执行右模糊查询(开头匹配前缀,最常用场景)
SELECT * FROM student WHERE sname LIKE '张%';
idx_sname_pre2 前缀索引;张三、张小 的记录,拿到对应主键:1、2、3;优势:没有全表扫描,依靠 B + 树二分定位,查询效率高。
SELECT * FROM student WHERE sname LIKE '%三';
前缀索引只按字符串开头字符构建 B + 树,无法匹配尾部字符;数据库无法使用该索引,放弃 B + 树检索,直接全表扫描。
SELECT * FROM student WHERE sname = '张三';
流程:通过前缀 张三 在 B + 树找到对应主键,回表校验完整姓名,正常使用索引。
ORDER BY sname 排序(排序依赖完整字符)。长文本(地址、备注、长昵称)如果建完整索引,B + 树节点存储的数据量大,树层级变高、IO 变多;截取合理前缀后,B + 树更紧凑、层级更低,查询更快。
截取长度太短 → 前缀重复多 → B + 树叶子节点大量重复值 → 区分度低,索引效果差;截取长度太长 → 失去 “缩减索引体积” 的意义。
原则:选择区分度足够高的最小前缀长度构建 B + 树。
对字符串字段截取前 N 个字符创建前缀索引,底层依旧采用B + 树结构存储:
查看索引,确认是前缀索引:
SHOW INDEX FROM student;
输出中会标记索引长度为 2,代表基于前 2 位字符构建 B + 树索引。
删除前缀索引:
DROP INDEX idx_sname_pre2 ON student;
在这个例子中,如何通过姓名列查找到张三丰这个人,给出查找树到数据的过程
结合上文中sname 前 2 位前缀索引(idx_sname_pre2) + B + 树结构,一步步拆解查找 张三丰 的完整流程,包含树检索、回表全过程。
student,主键 sid,对 sname 前 2 个字符建前缀索引。数据清单:| sid | sname | 截取前 2 位前缀 ||-----|--------|--------------|| 1 | 张三 | 张三 || 2 | 张三丰 | 张三 || 3 | 张小花 | 张小 || 4 | 李四 | 李四 || 5 | 李磊 | 李磊 || 6 | 王五 | 王五 |根节点(非叶子) 张三 李四 ↙ ↘叶子节点组1 叶子节点组2(张三,1) (张三,2) (张小,3) (李四,4) (李磊,5) (王五,6)
叶子节点存储格式:(前缀字符,主键 sid)
SELECT * FROM student WHERE sname = '张三丰';
查询目标完整姓名 张三丰,优先使用 sname(2) 前缀索引。截取目标字符串前 2 位:张三丰 → 前缀 = 张三。
张三 和根节点的分界关键字对比;在叶子组 1 中依次读取索引项:
(张三,1):前缀匹配,取出主键 sid=1;(张三,2):前缀匹配,取出主键 sid=2;(张小,3):前缀不匹配,停止当前分支检索。此时得到候选主键集合:
[1, 2]
前缀索引只存前缀 + 主键,没有完整姓名,必须回表:
sid=1、sid=2 去主键聚簇索引(另一棵 B + 树)查找整行数据;张三张三丰用完整字段和查询条件 '张三丰' 精准比对:
张三 → 不匹配,舍弃张三丰 → 完全匹配,保留最终返回:sid=2,sname=张三丰 整行记录。