一文搞懂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';
使用的是将整个字符串作为索引结构。
过程如下:
- 从index1索引树上找到索引值是"zhangsan@xx.com"的这条记录,去的ID2的值
- 到主键中查ID2的这一行,判断email的值是否是正确的,将这行记录装入结果集中;
- 再回到index1这个索引树上,继续判断下一条记录,发现不满足where条件,结束循环。
这个过程中只需要从主键索引树上查找一次数据,系统自认为扫描了一行。
使用前缀索引的执行过程
- 从index2的索引树上,找到满足索引值是“zhangs”的记录,找到第一个是ID1;
- 到主键索引树上查到ID1这一行,判断email的值满不满足where后的条件,不满足这一行丢弃。
- 继续回到index2这个索引树上查下一条记录,发现如果还是"zhangs",取出ID2,再回到ID2索引树上进行判断,如果值正确,将结果返回结果集中。
- 重复执行以上流程,直到从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'
两者的对比
相同点
- 都不支持范围查询,只能等值查询。
不同点 - 从查询效率上看,使用的hash字段方式的查询性能相对稳定一点,因为crc_32算出的值虽然有可能冲突,但是概率还是很小的。而倒序方式其实还是用的前缀索引的方式还会增加扫描行数。
- 从存储空间上看,倒序存储不会在主键上消耗额外的空间,但hash字段需要增加一个新字段。
- 从CPU消耗来讲,倒序每次写和读的时候都需要调用reverse函数;hash字段的方式需要嗲用crc32函数。从函数的复杂度讲,reverse效率更高一些。
总结
在向字符串类型的字段加索引的时候,需要考虑前缀索引是否合适,实在不行再加全字段索引。
- 全字段索引相比于前缀索引占用的空间多些。
- 创建前缀索引节省空间,但是会增加查询的扫描行数,并且加了之后不能使用覆盖索引。
- 倒序存储是基于前缀索引的改良版,用于字符串本身区分度不高的情况下。
- 创建hash字段索引,查询稳定但需增加一个额外的字段。
一文搞懂MySQL前缀索引的更多相关文章
- 一文搞懂mysql索引底层逻辑,干货满满!
一.什么是索引 在mysql中,索引是一种特殊的数据库结构,由数据表中的一列或多列组合而成,可以用来快速查询数据表中有某一特定值的记录.通过索引,查询数据时不用读完记录的所有信息,而只是查询索引列即可 ...
- 一文读懂MySQL的索引结构及查询优化
回顾前文: 一文学会MySQL的explain工具 (同时再次强调,这几篇关于MySQL的探究都是基于5.7版本,相关总结与结论不一定适用于其他版本) MySQL官方文档中(https://dev.m ...
- 一文搞懂MySQL体系架构!!
写在前面 很多小伙伴工作很长时间了,对于MySQL的掌握程度却仅仅停留在表面的CRUD,对于MySQL深层次的原理和技术知识了解的少之又少,随着工作年限的不断增长,职场竞争力却是不断降低的.很多时候, ...
- 一文搞懂│mysql 中的备份恢复、分区分表、主从复制、读写分离
目录 mysql 的备份和恢复 mysql 的分区分表 mysql 的主从复制读写分离 mysql 的备份和恢复 创建备份管理员 创建备份管理员,并授予管理员相应的权限 备份所需权限:select,r ...
- 一文搞懂MySQL事务的隔离性如何实现|MVCC
关注公众号[程序员白泽],带你走进一个不一样的程序员/学生党 前言 MySQL有ACID四大特性,本文着重讲解MySQL不同事务之间的隔离性的概念,以及MySQL如何实现隔离性.下面先罗列一下MySQ ...
- 一文读懂MySQL的事务隔离级别及MVCC机制
回顾前文: 一文学会MySQL的explain工具 一文读懂MySQL的索引结构及查询优化 (同时再次强调,这几篇关于MySQL的探究都是基于5.7版本,相关总结与结论不一定适用于其他版本) 就软件开 ...
- [转帖]一文看懂mysql数据库本质及存储引擎innodb+myisam
一文看懂mysql数据库本质及存储引擎innodb+myisam https://www.toutiao.com/i6740201316745740807/ 原创 波波说运维 2019-09-29 0 ...
- 一文搞懂所有Java集合面试题
Java集合 刚刚经历过秋招,看了大量的面经,顺便将常见的Java集合常考知识点总结了一下,并根据被问到的频率大致做了一个标注.一颗星表示知识点需要了解,被问到的频率不高,面试时起码能说个差不多.两颗 ...
- 一文搞懂Flink Window机制
Windows是处理无线数据流的核心,它将流分割成有限大小的桶(buckets),并在其上执行各种计算. 窗口化的Flink程序的结构通常如下,有分组流(keyed streams)和无分组流(non ...
随机推荐
- 题解-Koishi Loves Construction
题解-Koishi Loves Construction 前缀知识 质数 逆元 暴搜 Koishi Loves Construction 给定 \(X\),\(T\) 组测试数据,每次给一个 \(n\ ...
- 深入理解Java虚拟机(二)——HotSpot对象创建、内存、访问
对象的创建 虚拟机遇到一条字节码new指令时,开始对象创建过程. 首先去检查这个指令的参数是否能在常量池定位到一个类的符号引用: 检查这个符号引用代表的类是否已被加载.解析和初始化,如果没有就必须执行 ...
- ps查看完整程序执行路径
在linux下查看进程大家都会想到用 ps -ef|grep ***可是看到的不是全路径,怎么看全路径呢?每个进程启动之后在 /proc下面有一个于pid对应的路径例如:ps -ef|grep jav ...
- 把java编译成exe和安装包
由于某些项目甲方迟迟不结算尾款,这就很烦,只能想一些办法 我们知道java,python之类的代码是没有隐私可言的,那么怎么办,总要发给甲方验收,这就要做一些操作来确保自己的利益. 通过在源代码里加上 ...
- oracle查年度周末日期
1.查年度周末日期sql SELECT distinct TRUNC(TO_DATE('2019-01-01','yyyy-mm-dd')+ rownum,'iw')+ 5 AS sat, TRUNC ...
- rancher安装,快速安装
apt-get install docker.io docker -y docker run -d --restart=always -v /data/rancher_server:/var/lib/ ...
- 2020-2021-1 20209307《Linux内核原理与分析》第五周作业
一.理论知识 系统调用:操作系统为用户态进程与硬件设备进行交互提供的一组接口. 系统调用的三层皮:API(应用程序接口),中断向量system_call,中断服务程序sys_xyz 宏观上Linux操 ...
- Web服务器-正则表达式-正则其他(3.1.3)
@ 目录 其他api说明 关于作者 其他api说明 pattern = re.compile(r'\d+') m = pattern.match('6e812738712aaadad13') m.gr ...
- Linux下安装ffmpeg,视频格式转换
下载ffmpeg 从ffmpeg官网:http://ffmpeg.org/download.html 下载最新的ffmpeg安装包,然后通过如下命令解压: 解压 ffmpeg-*.tar.bz2 文件 ...
- Python字符串常用的一些东西
字符串的常用方法dir(str).查看某一方法的用法help(str.xxx). 1,索引和切片: 2,len():查看字符串的总长度. 3,+,拼接一个或多个字符串. 4,in,判定字符是否在字符串 ...