MySQL该怎么给字符串字段加索引?(二)

这是我参与11月更文挑战的第16天,活动详情查看:2021最后一次更文挑战

在上一节的学习中,我们学习了在MySQL中如何给字符串字段加索引,我们可以通过合理地创建前缀索引来增加我们检索的速度以及减小索引所占用的空间,那么我们该如何确定前缀索引的长度呢?

不难得出,只要联合索引的区分度够高,那么重复的就越少,这样我们回表去查询的次数就能降低。所以我们可以通过计算每个长度的选择性来判断要使用多长的前缀。

首先我们先算出完整列的选择性。

select count(distinct email)/count(*) from t_User;
复制代码

假设我们算出来的完整列的选择性为1,所以我们所选取的前缀如果越接近1,代表这个前缀索引的区分度越高。

select count(distinct left(email,2)/count(*) as s2),
       count(distinct left(email,3)/count(*) as s3),
       count(distinct left(email,4)/count(*) as s4),
       count(distinct left(email,5)/count(*) as s5),
复制代码

假设s2 = 0.8, s3 = 1, s4 = 1, s5 = 1,那我们根据选择性越接近完整列选择性以及字符越短占用空间少的原则,我们就可以选择前缀长度为3。

前缀索引对覆盖索引的影响

我们从上一章可以得出,前缀索引的使用可能会增加扫描的次数,那么接下来我们会发现影响不止如此。

我们来看个sql:

select id,email from t_User where email='[email protected]';
复制代码

如果我们用的是index1,也就是完整的字符串索引的话,因为在email索引树的叶子结点上就已经有id了,所以我们用上了覆盖索引,无序再回表去查询。但是如果用的是前缀索引的话,我们需要回表再去判断email的值是否和where后面的email的值相同。

就算我们将index2的长度改为18,虽然已经包含了整个字符串的长度,但是InnoDB还是要回到id索引去查,因为引擎并不确定提供的前缀索引是否截断了完整的信息,所以我们失去了覆盖索引给我们提供的优势。

那我们还有什么办法呢能给字符串字段添加索引?

  • 使用 hash 字段

我们可以在表上再创建一个整数字段,来保存邮箱的校验码,同时在这个字段上创建索引。

alter table t_User add emial_crc int unsigned, add index(emial_crc);
复制代码

然后每次插入新记录的时候,都同时用 crc32() 这个函数得到校验码填到这个新字段。由于 校验码可能存在冲突,也就是说两个不同的email通过 crc32() 函数得到的结果可能是相同的,所以查询语句 where 部分要判段email的值是否精确相同。

select id,email,name from t_User where email_crc=crc32('[email protected]') and email = '[email protected]';

复制代码

这样索引的长度就变成了4个字节。

小结

  1. 直接创建完整索引,这样可能比较占用空间;

  2. 创建前缀索引,节省空间,但会增加查询扫描次数,并且不能使用覆盖索引;

  3. 创建 hash 字段索引,查询性能稳定,有额外的存储和计算消耗,但不支持范围查询。

Guess you like

Origin juejin.im/post/7031935307192893447