一文搞懂MySQL前缀索引

一文搞懂MySQL前缀索引引入通常在开发中我们需要定义字符串类型的字段,例如用户名或者用户邮箱等。假设我们在维护一个用户登录系统,用户表的定义:createtableUser(IDbigintunsignedprimarykey,emailvarchar(64))engine=Innodb;如果使用邮箱登录的话,查询语句可能这样写:selectIDfromUserwhereemail=’xxx’;如果email字段没有加索引,那么这个语句只能做全表扫描。前缀索引MySQL是支持

大家好,又见面了,我是你们的朋友全栈君。

引入

通常在开发中我们需要定义字符串类型的字段,例如用户名或者用户邮箱等。
假设我们在维护一个用户登录系统,用户表的定义:


create table User(
ID bigint unsigned primary key,
email varchar(64)

)engine=Innodb;

如果使用邮箱登录的话,查询语句可能这样写:

select ID  from User where email='xxx';

如果email字段没有加索引,那么这个语句只能做全表扫描。

前缀索引

MySQL是支持前缀索引的,也就是说,你可以定义字符串的一部分作为索引。如果不指定前缀索引,那么索引就是整个字符串。

例子:

alter table User add index index1(email);
alter table User add index index2(email(6));

第一句SQL创建的索引就是将email整个字符串作为索引;第二个SQL语句创建的索引,只取email字符串的前6个字节作为索引。

存储过程中的具体区别如下图所示

在这里插入图片描述

在这里插入图片描述

显然可以从图中知道,email(6)这个索引结构中每个邮箱字段只取前6个字节,所以占用的空间更少,这就是使用前缀索引的优势。

缺点:
可能会额外的增加记录扫描的次数。

这个该怎么理解呢?

select id,name,email from User where email =' zhangsan@xx.com';

使用的是将整个字符串作为索引结构。
过程如下:

  1. 从index1索引树上找到索引值是”zhangsan@xx.com”的这条记录,去的ID2的值
  2. 到主键中查ID2的这一行,判断email的值是否是正确的,将这行记录装入结果集中;
  3. 再回到index1这个索引树上,继续判断下一条记录,发现不满足where条件,结束循环。

这个过程中只需要从主键索引树上查找一次数据,系统自认为扫描了一行。

使用前缀索引的执行过程

  1. 从index2的索引树上,找到满足索引值是“zhangs”的记录,找到第一个是ID1;
  2. 到主键索引树上查到ID1这一行,判断email的值满不满足where后的条件,不满足这一行丢弃。
  3. 继续回到index2这个索引树上查下一条记录,发现如果还是”zhangs”,取出ID2,再回到ID2索引树上进行判断,如果值正确,将结果返回结果集中。
  4. 重复执行以上流程,直到从index2索引树上取出的数据不是“zhangs”,循环结束。

通过以上执行流程的分析你就可以知道,前缀索引会导致扫描的行数变多,这和你所指定前缀的长度有关。或许email(7)中的区分度就比email(6)高,就不会扫描那么多行。

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

那怎样定义前缀索引长度比较好呢?

实际上,建立索引时关注的是区分度,区分度越高,越能体现索引的价值和他的优势。因此我们可以通过统计索引上有不同的值来判断要使用多长的前缀。

select count(distinct email) as L from User;

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

前面我们说了使用前缀索引可能会增加扫描行数,这会影响性能。其实前缀索引的影响不止如此:

select id ,email from User where email='zhangsan@xx.com';
select id , name, email from User where email='zhangsan@xx.com';

第一句SQL相比于第二条SQL,只返回了id和email。如果使用email整个字符串作为索引的话,可以利用覆盖索引,从index1查到结果直接返回,不需要回表。但是如果使用前缀索引的话,是需要回表进行判断的。

倒序存储与Hash存储

在选取索引的时候,我们需要明白:索引选取的越长,占用的磁盘空间就越大,相同的数据页能放下的索引值就越小,搜索的效率也就会越低。

如果我们在区分度不是很高的场景下,前缀索引的效果就不明显了,我们该如何才去措施提高查询效率。

采用倒序存储方式

select filed_list from t where id_card = reverse('input_id_card')

因为字符串正序的区分度不够明显所以可以看看如果采用倒序的话情况如何,如果倒序的区分度更高,可以采用这种方式。

采用Hash字段

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

这里在表t中多加入了一个字段 id_card_crc并把它作为索引。
然后每次插入新纪录的时候,都用crc32函数得到校验码填充到这个新字段中。由于产生的校验码也有可能冲突(相同)所以查询条件部分需要判断id_card的值是否相同。


select field_list from t
where id_card_crc=crc32('input_id_card_string')
and 
id_card='input_id_card_string'

两者的对比

相同点

  1. 都不支持范围查询,只能等值查询。
    不同点
  2. 从查询效率上看,使用的hash字段方式的查询性能相对稳定一点,因为crc_32算出的值虽然有可能冲突,但是概率还是很小的。而倒序方式其实还是用的前缀索引的方式还会增加扫描行数。
  3. 从存储空间上看,倒序存储不会在主键上消耗额外的空间,但hash字段需要增加一个新字段。
  4. 从CPU消耗来讲,倒序每次写和读的时候都需要调用reverse函数;hash字段的方式需要嗲用crc32函数。从函数的复杂度讲,reverse效率更高一些。

总结

在向字符串类型的字段加索引的时候,需要考虑前缀索引是否合适,实在不行再加全字段索引。

  1. 全字段索引相比于前缀索引占用的空间多些。
  2. 创建前缀索引节省空间,但是会增加查询的扫描行数,并且加了之后不能使用覆盖索引。
  3. 倒序存储是基于前缀索引的改良版,用于字符串本身区分度不高的情况下。
  4. 创建hash字段索引,查询稳定但需增加一个额外的字段。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/146238.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • android错误之Unable to resolve target ‘Google Inc.:Google APIs:6’

    在导入一个项目是,出现Unable to resolve target ‘Google Inc.:Google APIs:6’ 按下面方式解决: 修改目录下的project.property文件内容为target=Google Inc.:Google APIs:16(在这里他本来可能是其他版本号,不用管它,只需要改成你所导入的包的版本就行,比如我这里已经导入就是api1

    2022年3月10日
    45
  • decltype函数_decltype有什么用

    decltype函数_decltype有什么用前言如果想要知道一个表达式的数据类型但又不想对表达式进行求值,那么auto似乎就不满足这中要求,c++11加入了decltype操作符,它的功能与auto类似,都是进行类型推导,但是decltype就能对某一表达式的类型进行推导并且不求表达式的值.因为decltype在很多的时候并不同于auto操作符,所以我会将大部分的不同罗列出来,比如:数组,顶层const等.declt…

    2022年9月8日
    1
  • 黑盒测试的常见的测试用例设计方法有哪些[通俗易懂]

    黑盒测试的常见的测试用例设计方法有哪些[通俗易懂]测试用例怎么设计?一般根据业务知识掌握,之前已有的回归测试用例,测试知识库,测试需求开始设计。黑盒测试的常见的测试用例设计方法有哪些?1)等价类划分:等价类是指某个输入域的子集合.在该子集合中,各个输入数据对于揭露程序中的错误都是等效的.并合理地假定:测试某等价类的代表值就等于对这一类其它值的测试.因此,可以把全部输入数据合理划分为若干等价类,在每一个等价类中取一个数据作为测试的输入条件,就可以用少量代表性的测试数据.取得较好的测试结果.等价类划分可有两种不同的情况:有效等价类和无效等价类.

    2022年6月29日
    21
  • 如何使用eclipse创建JAVA项目并写一个简单的HelloWorld

    如何使用eclipse创建JAVA项目并写一个简单的HelloWorld打开eclipse软件,选择好工作区域(就是项目的储存地址)后登陆。File-New-Project选择JavaProject输入项目名称点击完成(Finish)在SRC(SRC是专门放java源代码的文件夹,就是你在IDE里编写的各个java类的文件都在里面)中新建package包包的命名规范:包名全部使用小写。包名通常由若干个标识符…

    2022年7月24日
    5
  • RadControls for Windows 8

    RadControls for Windows 8RadControlsforWindows8http://www.telerik.com/help/wpf/radchartview-populating-with-data-static-data.htmlRadCartesianChartchart=newRadCartesianChart();      chart.HorizontalAxi

    2022年7月19日
    9
  • Django组件_django路由path

    Django组件_django路由path什么是Django中间件中间件(Middleware)是一个用来处理Django的请求(Request)和响应(Response)的框架级别的钩子,它是一个轻量、低级别的插件系统,用于在全局范围内改

    2022年7月29日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号