字符串字段添加索引
MySQL
是支持前缀索引的,也就是说,你可以定义字符串的一部分作为索引。默认地,如果你创建索引的语句不指定前端长度,那么索引就会包含整个字符串。
# 包含整个字符串
alter table table_name add index index1(column);
# 对每个记录都只取前6个字节
alter table table_name add index index2(column(6));
根据字节创建索引,占用空间小,但是可能会导致扫描次数增多。
举个栗子
假设有车辆表vehicle
。
create table vehicle
(
id varchar(32) not null
primary key,
brand varchar(10) not null comment '车辆品牌',
plate_number varchar(10) not null comment '车牌号'
)
comment '车辆信息表';
alter table vehicle add index brand_index(brand(3));
INSERT INTO `daily_test`.`vehicle`(`id`, `brand`, `plate_number`) VALUES ('1', '比亚迪-汉DMI', '鲁B.11111');
INSERT INTO `daily_test`.`vehicle`(`id`, `brand`, `plate_number`) VALUES ('2', '比亚迪-秦DMI', '鲁B.22222');
INSERT INTO `daily_test`.`vehicle`(`id`, `brand`, `plate_number`) VALUES ('3', '比亚迪-宋DMI', '鲁B.33333');
现在有根据车辆品牌查询车辆的需求,那么我们需要执行SQL
:
select id,brand from vehicle where brand = '比亚迪-汉DMI';
当brand
字段使用字节索引brand_index
时,查询到数据流程:
- 从
brand_index
索引树查询到值是比亚迪
的记录,找到第一个值id1。 - 到主键上查询到主键值是id1的数据行,判断brand字段的值是否为
比亚迪-汉DMI
,如果是,把本行数据加入结果集,继续执行步骤一、步骤二。如果不是,丢弃本行,找下一个。 - 直到在索引树上取到的值不是
比亚迪
时,循环结束。
当使用全字段索引时,根据索引树查到符合where条件的id,根据id去查询数据所在的行即可,无需再判断。
实测
可以看到,当使用前缀索引时,扫描行数为3,当使用字段索引时,扫描行数为1。
使用前缀索引时,需要定义好长度,否则虽然节省了空间,但是可能导致查询成本增加,影响性能。
如何选择合适的前缀索引长度
我们在建立索引时关注的是区分度,区分度越高越好。因为区分度越高,意味着重复的键值越少。因此,我们可以通过统计索引上有多少个不同的值来判断要使用多长的前缀。
如使用一下语句选择前缀索引的长度:
select
count(distinct left(email,4))as L4,
count(distinct left(email,5))as L5,
count(distinct left(email,6))as L6,
count(distinct left(email,7))as L7 from SUser;
创建前缀索引前,需要预定可接受的损失比例,使用Ln/总区分度计算损失比例,选择可达到最小损失比例的前缀长度即可。
字符串索引的四种方式
- 直接创建完整索引,这样可能比较占用空间(应用最广泛);
- 创建前缀索引,节省空间,但会增加查询扫描次数,并且不能使用覆盖索引;
- 倒序存储,再创建前缀索引,用于绕过字符串本身前缀的区分度不够的问题;
- 创建 hash 字段索引,查询性能稳定,有额外的存储和计算消耗,跟第三种方式一样,都不支持范围扫描。