背景
在本周的时候,接到一个需求,需要通过加密后的身份证 md5 去数据库里匹配。由于业务方存储的是身份证 md5+username 构建的一列,并且没有加索引。
解决方案:1.新建一列 md5的列,加索引 2.对现有的列进行加前缀索引。
并且由于是md5值,使用前缀索引区分度很高。但是为了后期业务的拓展,采用了方案1。
那么结合遇到的场景,就来聊聊前缀索引的使用场景细节。
前缀索引
在平时的使用过过程,其实很多网站支持使用邮箱进行登陆。所以可能出现。
select f1, f2 from SUser where email='xxx';
create table SUser(
ID bigint unsigned primary key,
email varchar(64),
email_2 varchar(64)
)engine=innodb;
那么针对email字段 如何添加索引比较好。其实如果email的区分度比较好的话,可以直接对email加整个索引。 或者针对指定前缀索引。
alter table SUser add index index1(email);
alter table SUser add index index2(email_2(6));
index1索引图结构如下
index_2 索引如下
接着我们来分析index1 和 index2索引的查询过程。
其实针对于index1来说,由于对email整个字段添加了索引,当查询 email = ‘qxlxi@qq.com’ 会直接从索引树上查询到满足条件的记录。找到对应的id,然后通过主键索引树上找到整行记录。然后接着判断是否存在图和条件的,没有直接退出。
但是对于index2来说,由于只针对部分加索引,会查询符合条件的索引,比如找到第一条,然后找到id,发现不符合,接着查询,所以整个过程需要不断的拿主键id 判断等。所以合理的设置前缀索引的长度 非常有讲究,否则,可能会增加额外的记录扫描次数。
用前缀索引,定义好长度,就可以做到既节省空间,又不用额外增加太多的查询成本。
其实主要还是看字段长度的区分度,区分度高就可以避免额外查询。
select count(distinct email) as L from SUser;
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;
说白了就是,进行对比,选择一个区分度比较高的长度。
对覆盖索引的影响
EXPLAIN SELECT id,email FROM Suser WHERE email = 'qxlxi@qq.com';
EXPLAIN SELECT id,email FROM Suser WHERE email_2 = 'qxlxi@qq.com';
可以发现,使用前缀索引没有办法进行使用覆盖索引,而整个字符串的可以使用。这是因为对于前缀索引需要在回到id索引查下具体的值是否符合条件。
其他方式
比如针对身份证这种,前6位区分度不高,可以采用倒叙存储的方式。或者针对特定列创建对应的额外的索引字段。
select field_list from t where id_card = reverse('input_id_card_string');
针对特定学号,城市编号和@gamil.com 没有区分度,这种情况我们可以采用只保存学校编号入学年份、顺序编号即可。
小结
本篇文章,主要简单的介绍了下,使用字符串创建前缀索引的一些优缺点。
1.创建整个字符串可能比较浪费空间
2.创建前缀索引,节省空间,但是可能有多余的查询。
3.使用一定的区分度减少多余查询。