倒序查10条数据_10 | 怎么给字符串字段加索引?

现在,几乎所有的系统都支持邮箱登录,如何在邮箱这样的字段上建立合理的索引,是我们今天要讨论的问题。

假设,你现在维护一个支持邮箱登录的系统,用户表是这么定义的:

mysql> create table SUser(
ID bigint unsigned primary key,
email varchar(64), 
... 
)engine=innodb; 

由于要使用邮箱登录,所以业务代码中一定会出现类似于这样的语句:

mysql> select f1, f2 from SUser where email='xxx';

如果 email 这个字段上没有索引,那么这个语句就只能做全表扫描。同时,MySQL 是支持前缀索引的,也就是说,你可以定义字符串的一部分作为索引。默认地,如果你创建索引的语句不指定前缀长度,那么索引就会包含整个字符串。

比如,这两个在 email 字段上创建索引的语句:

mysql> alter table SUser add index index1(email);
或
mysql> alter table SUser add index index2(email(6));

第一个语句创建的 index1 索引里面,包含了每个记录的整个字符串;而第二个语句创建的 index2 索引里面,对于每个记录都是只取前 6 个字节。

那么,这两种不同的定义在数据结构和存储上有什么区别呢?如图 所示,就是这两个索引的示意图。

40e3da6b4711b5bdbdda721b74d56dee.png

ccc254752614a4f73d2d8df413edac80.png

从图中你可以看到,由于 email(6) 这个索引结构中每个邮箱字段都只取前 6 个字节(即:zhangs),所以占用的空间会更小,这就是使用前缀索引的优势。但,这同时带来的损失是,可能会增加额外的记录扫描次数。

接下来,再看看下面这个语句,在这两个索引定义下分别是怎么执行的。

select id,name,email from SUser where email='zhangssxyz@xxx.com';

如果使用的是 index1(即 email 整个字符串的索引结构),执行顺序是这样的:

  1. 从 index1 索引树找到满足索引值是’zhangssxyz@xxx.com’的这条记录,取得 ID2 的值;
  2. 到主键上查到主键值是 ID2 的行,判断 email 的值是正确的,将这行记录加入结果集;
  3. 取 index1 索引树上刚刚查到的位置的下一条记录,发现已经不满足 email='zhangssxyz@xxx.com’的条件了,循环结束。

这个过程中,只需要回主键索引取一次数据,所以系统认为只扫描了一行。

如果使用的是 index2(即 email(6) 索引结构),执行顺序是这样的:

  1. 从 index2 索引树找到满足索引值是’zhangs’的记录,找到的第一个是 ID1;
  2. 到主键上查到主键值是 ID1 的行,判断出 email 的值不是’zhangssxyz@xxx.com’,这行记录丢弃;取 index2 上刚刚查到的位置的下一条记录,发现仍然是’zhangs’,取出 ID2,再到 ID 索引上取整行然后判断,这次值对了,将这行记录加入结果集;
  3. 重复上一步,直到在 idxe2 上取到的值不是’zhangs’时,循环结束。

在这个过程中,要回主键索引取 4 次数据,也就是扫描了 4 行。

通过这个对比,你很容易就可以发现,使用前缀索引后,可能会导致查询语句读数据的次数变多。

但是,对于这个查询语句来说,如果你定义的 index2 不是 email(6) 而是 email(7),也就是说取 email 字段的前 7 个字节来构建索引的话,即满足前缀’zhangss’的记录只有一个,也能够直接查到 ID2,只扫描一行就结束了。

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

当要给字符串创建前缀索引时,有什么方法能够确定我应该使用多长的前缀呢?

实际上,在建立索引时关注的是区分度,区分度越高越好。因为区分度越高,意味着重复的键值越少。因此,我们可以通过统计索引上有多少个不同的值来判断要使用多长的前缀。

首先,你可以使用下面这个语句,算出这个列上有多少个不同的值:

mysql> select count(distinct email) as L from SUser;

然后,依次选取不同长度的前缀来看这个值,比如我们要看一下 4~7 个字节的前缀索引,可以用这个语句:

mysql> 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;

当然,使用前缀索引很可能会损失区分度,所以你需要预先设定一个可以接受的损失比例,比如 5%。然后,在返回的 L4~L7 中,找出不小于 L * 95% 的值,假设这里 L6、L7 都满足,你就可以选择前缀长度为 6。

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

使用前缀索引可能会增加扫描行数,这会影响到性能。其实,前缀索引的影响不止如此,我们再看一下另外一个场景

来看看这个 SQL 语句:

select id,email from SUser where email='zhangssxyz@xxx.com';

与前面例子中的 SQL 语句

select id,name,email from SUser where email='zhangssxyz@xxx.com';

相比,这个语句只要求返回 id 和 email 字段。

所以,如果使用 index1(即 email 整个字符串的索引结构)的话,可以利用覆盖索引,从 index1 查到结果后直接就返回了,不需要回到 ID 索引再去查一次。而如果使用 index2(即 email(6) 索引结构)的话,就不得不回到 ID 索引再去判断 email 字段的值。

即使你将 index2 的定义修改为 email(18) 的前缀索引,这时候虽然 index2 已经包含了所有的信息,但 InnoDB 还是要回到 id 索引再查一下,因为系统并不确定前缀索引的定义是否截断了完整信息。

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

其他方式

对于类似于邮箱这样的字段来说,使用前缀索引的效果可能还不错。但是,遇到前缀的区分度不够好的情况时,我们要怎么办呢?

比如,我们国家的身份证号,一共 18 位,其中前 6 位是地址码,所以同一个县的人的身份证号前 6 位一般会是相同的。假设你维护的数据库是一个市的公民信息系统,这时候如果对身份证号做长度为 6 的前缀索引的话,这个索引的区分度就非常低了。

按照我们前面说的方法,可能你需要创建长度为 12 以上的前缀索引,才能够满足区分度要求。但是,索引选取的越长,占用的磁盘空间就越大,相同的数据页能放下的索引值就越少,搜索的效率也就会越低。

那么,如果我们能够确定业务需求里面只有按照身份证进行等值查询的需求,还有没有别的处理方法呢?这种方法,既可以占用更小的空间,也能达到相同的查询效率。答案是,有的。

第一种方式是使用倒序存储。如果你存储身份证号的时候把它倒过来存,每次查询的时候,你可以这么写:

mysql> select field_list from t where id_card = reverse('input_id_card_string');

由于身份证号的最后 6 位没有地址码这样的重复逻辑,所以最后这 6 位很可能就提供了足够的区分度。当然了,实践中你不要忘记使用 count(distinct) 方法去做个验证。

第二种方式是使用 hash 字段。你可以在表上再创建一个整数字段,来保存身份证的校验码,同时在这个字段上创建索引。

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

这样,索引的长度变成了 4 个字节,比原来小了很多。

接下来,看看使用倒序存储和使用 hash 字段这两种方法的异同点。

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

它们的区别,主要体现在以下三个方面:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/534501.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

保留小数点后三位_【Meta分析】Stata制作森林图时,如何保留三位小数?

系统评价/Meta分析指全面收集所有相关研究并逐个进行严格评价和分析,再用定性或定量合成的方法对资料进行处理得出综合结论的研究方法。在指导学员的过程中发现初学者在学习过程中常常会碰到许多共性问题,本公众号特此开设专栏解答,希望能够和…

android自动计步_Android计步模块实例代码(类似微信运动)

最近在项目中研究计步模块,每天0点开始记录当天的步数,类似微信运动。碰到了不少坑今天有时间整理出来给大家看看。做之前在google、baidu、github上搜了个遍没找到好的,大多数都是需要在后台存活,需要后台Service。对于现在的各大…

python井字棋ai_实现AI下井字棋的alpha-beta剪枝算法(python实现)

代码参考自中国大学mooc上人工智能与信息社会陈斌老师的算法,我在原来的基础上增加了玩家输入的异常捕获 AlphaBeta剪枝算法是对Minimax方法的优化,能够极大提高搜索树的效率,如果对这个算法感兴趣的可以去参考相关资料。 当正确理解AlphaBet…

Redis小计(2)

目录 1.exists命令 2.del命令 3.expire/pexpire命令 4.ttl命令 5.redis对于key过期的删除策略 1.exists命令 exists X1 X2 X3 X4:返回四个key存在的个数。 2.del命令 del X1 X2:删除key。 3.expire/pexpire命令 给key设置超时时间。 expire key…

unity 彩带粒子_iOS动画开发----粒子系统---彩带效果

参考博文地址:http://my.oschina.net/u/2340880/blog/485095?fromerrbgjLq4Mw一、粒子发射器iOS中的粒子效果有两部分组成,一部分为发射器,设置例子发射的宏观属性,另一部分是粒子单元,用于设置相应的粒子属性。粒子发射器是基于…

一秒执行一次_《一秒钟》:一贯的粗旷式抓大放小,张艺谋的自命题作业总是要观众自己再做一遍...

还有不变的永远在奔跑的大棉裤花棉袄的圆脸妮子,这是导演张艺谋最新作品《一秒钟》的最直接观感。张艺谋是个善于从普世情怀处挖掘题材的导演。之前诸多现实题材类型作品,诸如讲父子和解的《千里走单骑》、夫妻爱情的《归来》以及《我的父亲母亲》&#…

latex 作者加小标_Latex 写期刊论文的小技巧

在不同文字处理系统(如 MiKTeX, TeX Live, CTeX, cwTex) 或 不同整合开发环境 ( 如Texstudio, WinEdt, TeXstudio, TeXmaker) 中,我用了 Miktex Texstudio 的常用组合 (win10环境中)。1: 先MiKTeX,后Texstudio ;2: 安装包(packages);3&#…

unity 畸变_unity3d 几种镜头畸变

1.Fisheye distortion 鱼眼镜头解释来自百度百科:鱼眼镜头是一种焦距为16mm或更短的并且视角接近或等于180。 它是一种极端的广角镜头,“鱼眼镜头”是它的俗称。为使镜头达到最大的摄影视角,这种摄影镜头的前镜片直径很短且呈抛物状向镜头前…

restfull加签_SpringBoot RestFull API签名

一、需求如下对指定的API路径进行签名认证,对于没有指定的无需认证,认证具体到方法。二、查阅资料与开发1.了解JWT,实际上用的开源jjwt2.编写自定义注解3.编写拦截器,主要是拦截特定的url进行签名验证,这里解析请求的h…

mysql 5.5.18下载_MySQL5.7.18下载和安装过程图文详解

MySql下载1、打开官网找到下载路口,这里直接给出下载的地址2、选择64位版本3、直接下载MySql5.7.18.1安装过程1 、运行安装软件,接受协议2、选择默认安装3、下一步到检查环境界面,点击“Execute”执行检查 (可以后面单独下载插件安装)&…

mysql找不到performance_Mysql安装完毕运行时没有mysql和performance_schema数据库_MySQL

Mysql问题 ERROR 1045 (28000): Access denied for user ‘root’’localhost’ (using password: YES)Mysql安装完毕运行时没有 mysql 和 performance_schema 数据库问题一:之前卸载未卸载干净问题二:没有管理员权限进入问题三:登录时&#…

mysql latid1_mysql触发器的实战经验

1 引言Mysql的触发器和存储过程一样,都是嵌入到mysql的一段程序。触发器是mysql5新增的功能,目前线上凤巢系统、北斗系统以及哥伦布系统使用的数据库均是mysql5.0.45版本,很多程序比如fc-star管理端,sfrd(das),dorad…

mysql数据库sql注入原理_SQL注入原理解析以及举例1

sql注入是指web应用程序对用户输入数据的合法性没有判断,导致攻击者可以构造不同的sql语句来实现对数据库的操作。sql注入漏洞产生满足条件:1;用户能够控制数据的输入。2;原本需要执行的代码,拼接了用户的输入。举例&a…

mysql存储map数据结构_map数据结构

Go map实现原理 - 恋恋美食的个人空间 - OSCHINA - 中文开源技术交流社区 https://my.oschina.net/renhc/blog/2208417// A header for a Go map.type hmap struct {// Note: the format of the hmap is also encoded in cmd/compile/internal/gc/reflect.go.// Make sure this…

四因素三水平正交表_做论文要用正交表?我打包送给你

正交试验目前在国内的应用量仍然是比较高的,许多高校毕业生喜欢利用正交试验来获取研究数据,最终完成毕业论文的撰写或者期刊投稿。正交试验方案的设计,必然要用到(标准)正交表。那么大家都是从哪里获取正交表的呢?小兵给这方面的…

plsql视图添加表字段_Oracle-单表多字段查询(不使用*)

环境:Oracle 11g,plsql 14目的:不使用*,查询拥有上百个字段的表的所有字段。懒人大法:在文章末尾。sql实现逻辑:1、首先建一张100个字段以上的表,通过excel的方式将表建好后直接复制粘贴到plsql的建表界面。…

mysql 编译安装与rpm安装的区别_编译安装与RPM安装的区别

建议在安装线上的生产服务器软件包时都用源码安装,这是因为源码安装可以自行调整编译参数,最大化地定制安装结果。这里以MySQL 5线上环境的编译安装来说明之,其编译参数如下所示:./configure-prefix/usr/local/mysql -without-deb…

python字符串变量s的值是python网络爬虫_【Python爬虫作业】-字符串

一、定义字符串变量1.请定义三个字符串a,b,c值分别为 I,like, python2.请将上面三个变量合并输出I like pythonaIblikecpythonprint(a)print(b)print(c)print(a,b,c)二、定义一个变量 s sdghHhf 1.请先将变量s的空白符去掉 赋值给新变量s1 打印输出2.请分别将s1变为全部大写(命…

lableimg闪退_CV学习笔记(二十五):数据集标注与制作

最近在做一些数据标注的工作,虽然标注数据比较枯燥,但这也是每个做算法的工程师升级打怪的必由之路。使用一些合适的工具往往可以事半功倍,效率UP。一:数据标注流程二:数据处理的一些小代码1:重命名当得到这…

mysql show profile详解_SQL 性能分析利器 show profile

本文首发个人公众号《andyqian》, 期待你的关注~前言在之前的文章中,我们提到过一些慢SQL优化的步骤。其中就包括:使用 explain 关键字来查看执行计划,是否命中索引。通过计算某列的区分度,来判断该列是否适合新建索引…