MySQL---字符串字段“优雅”的加索引

字符串类型是一种很常见存储类型,但是如何优雅的给字符串类型的字段添加索引来优化查询速度呢?

使用前缀索引

这里假如我们有一张表

create table User(
ID bigint unsigned primary key,
id_card varchar(18),
email varchar(64),
...
)engine=innodb;

有一条SQL语句 select f1, f2 from User where email='xxx'; (f1、f2表示表中的两个字段),因为没有添加索引,此时走的肯定是全表扫描。

我们可以给email加上索引alter table User add index index1(email);

因为MySQL支持前缀索引,可以加上只取规定的前6个字节的索引 alter table SUser add index index2(email(6));

index1和index2的区别是index2索引结构中每个邮箱字段都只取前6个字节,所以占用的空间会更小,这就是使用前缀索引的优势。

虽然index2占用的空间更小了,但是当我们的表中含有大量相同的数据时候,由于前6位并不能准确的判断,所以需要进行回表,这就导致扫描行数增多。

所以使用前缀索引,定义好长度,就可以做到既节省空间,又不用额外增加太多的查询成本

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

对于SQL select id,email from SUser where email='[email protected]';是可以用到覆盖索引的,当我们使用index1时,MySQL会使用覆盖索引。但是当使用index2时,由于email的不完整性,不得不回到id索引再去判断email字段的值。即使你将index2的定义修改为email(18)的前缀索引,这时候虽然index2已经包含了所有的信息,但InnoDB还是要回到id索引再查一下,因为系统并不确定前缀索引的定义是否截断了完整信息。

因此用前缀索引就无法使用覆盖索引对查询性能的优化了,这也是在选择是否使用前缀索引时需要考虑的一个因素。

使用倒序存储

假如我们要存储表中的id_card身份证号信息,由于身份证号前6位的重复性很高,我们可以存储身份证号的时候把它倒过来存,查询的时候只需要这样写:

select field_list from user where id_card = reverse('xxxxxxxxxxxxx');

使用hash字段

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

alter table user add id_card_crc int unsigned, add index(id_card_crc);

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

倒序存储和hash字段区别

相同点:

两者都不支持范围查询。倒序存储的字段上创建的索引是按照倒序字符串的方式排序的,已经没有办法利用索引方式查出身份证号码在[ID_X, ID_Y]的所有user。同样hash字段的方式也只能支持等值查询。

不同点:

  1. 从占用的额外空间来看,倒序存储方式在主键索引上,不会消耗额外的存储空间,而hash字段方法需要增加一个字段。当倒序存储方式使用前缀长度过长时,这个消耗跟额外这个hash字段也差不多抵消了。
  2. 在CPU消耗方面,倒序存储方式每次写和读的时候,都需要额外调用一次reverse函数,而hash字段的方式需要额外调用一次crc32()函数。如果只从这两个函数的计算复杂度来看的话,reverse函数额外消耗的CPU资源会更小些。
  3. 从查询效率上看,使用hash字段方式的查询性能相对更稳定一些。因为crc32算出来的值虽然有冲突的概率,但是概率非常小,我们可以认为每次查询的平均扫描行数接近1。而倒序存储方式本质还是前缀索引的方式,增加扫描行数的概率大于hash字段存储。

猜你喜欢

转载自blog.csdn.net/MAKEJAVAMAN/article/details/118527293