字符串建立索引的优化

1. 建立前缀索引

假设建立一个支持邮箱登录的用户表,对于邮件字段来说,可以有以下几种建立索引的方式:

  1. 直接对整个字符串建立索引

    alter table SUser add index index1(email);
  2. 对整个字符串的前一部分建立索引 - 前缀索引

    alter table SUser add index index2(email(6));

方式 2 相较于 方式 1 来说,利用前缀索引,占用的空间更小。但有可能造成性能的损失,读取数据的次数变多。

假设在 user 表中存在zhangsp1234@gmail.com, zhangs1pzxyz@qq.com , zhangssxyz@xxx.com, 三条记录。

有这样一条语句 select id,name,email from SUser where email='zhangssxyz@xxx.com';

在使用 index1 索引时,流程如下:

  1. 在 index1 中,找到名字是 zhangssxyz@xxx.com 的记录,获取 ID.
  2. 在主键索引上对应 ID的行,判断 email 是否正确,将记录加入结果集。
  3. 接着取 index1 索引的下一条记录,发现不满足 email 格式,结束循环。

如果使用 index2 索引:

  1. 在 index2 中,找到名字是 zhangs 的记录,获取 ID.
  2. 在主键索引上对应 ID的行,这时拿到的是 zhangss1234@gmail.com 的行, 发现不符合,丢弃。
  3. 接着在 index2 循环,拿到下一条记录 ID。
  4. 在主键索引上对应 ID的行,这时拿到的是 zhangsspzxyz@qq.com 的行, 发现不符合,丢弃。
  5. 接着在 index2 循环,拿到下一条记录 ID。
  6. 在主键索引上对应 ID的行,这时拿到的是 zhangssxyz@xxx.com 的行, 发现符合,纳入结果集。
  7. 接着在 index2 循环,发现记录格式不符合,结束循环。

看这个过程,很容易发现,前缀索引会增加查询语句读取数据的次数。

但如果将前缀索引的 email(6) 改成 email(7),就会减少查询的次数,对应在主键索引上只搜索一次。这就说明,如果能合适的设置前缀索引的长度,就能在空间和效率上取得平衡。

如何找到合适的前缀索引长度

在建立索引时,应该去关注区分度,区分度越高,则说明重复的键值越少。

可以通过执行查询来统计列上有多少不同的值。

mysql> select
count(distinct email)as L,
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%, 用 L 的数量 * 区分度比例(1-5%=95%),然后看在 L4 到 L7 中哪个满足。

前缀索引的影响

在之前覆盖索引的文章中,如果查询的列的信息被包含在二级索引上,那么就可以避免回表的过程,进而减少查询次数,提供效率。但如果在建立索引时,使用了前缀索引,那么无论满不满足覆盖索引的规则,都会回表。因为系统不能确定前缀索引是否截取了完成信息,进而必须做一次判断。

也就是说,前缀索引除了会增加查询语句的次数,还会禁止使用覆盖索引。

2. 倒序存储

对于邮箱这类的字符串来说,由于前几位有较大的区分度,所以用前缀索引还不错。但如果是区分度不好的情况,比如身份证,前 6 位都是地址码,很多人都会一样。这时如果想要使用前缀索引,就需要至少 12 位以上,对应查询效率和空间都不是很合适。

一个比较好的办法是将字符串倒序存储,将区分度高的字符开头。

例如:

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

3. 使用 hash 字段

在网络传输时,CRC - 循环冗余校验被用于检验文件。对应在 MySQL 里也有这个函数,crc32().

该函数的返回范围是 0-4294967296 也就是 4 字节,相对于其他字符串来说,属于较短的长度。

在创建表时,可再创建一个整数字段,来保存这类字符串,如身份证的校验码(crc32()的返回值), 并为该字段创建索引。

如:

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

在插入记录时,将 crc32() 的结果插入到记录中。

但由于 crc32() 只有 32 位的特性,容易发生 hash 碰撞,就是说可能两个字符串经过计算后得到相同的验证码。这时就存在冲突,所以还需要判断下查询的值是否一致。

如:

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

总结

我们知道,MySQL 中使用的是 B+ 树来存储索引的,这自然就是有序的,所以前缀查询就支持范围查询。

而 Hash 字段和倒序查询两种方式就不行了,倒序查询是按照倒序字符串存储的,而 hash 字段和字符串本身也没有关系,这就意味着这两种方式是不支持范围查询的。

在占用空间上来说,倒序存储占用的是和普通索引的一样的空间。而 hash 字段,需要增加一个字段来存在 hash 校验码。

在 CPU 消耗,倒序时,每次读和写都需要调用 reverse 函数。hash 方式需要额外调用 crc32() 函数。两个函数实现来看,reverse 函数 CPU 消耗会少些。

在查询效率上,hash 字段查询性能更好稳定些。虽然可能存在冲突的情况,但概率很小。而倒序存储还是用前缀索引的方式,会额外增加扫描行数。

总结一下,一般提高查询字符串的效率有如下方式:

  • 直接创建完成索引,但占用空间较大。
  • 创建前缀索引,节省空间,但会增加扫描次数,不能利用覆盖索引。
  • 倒序存储,再创建前缀索引,节省空间,增加扫描次数,不能利用覆盖索引。
  • hash 字段,性能稳定,但占用额外的空间,不支持范围查询。

MySQL 字符串索引优化方案的更多相关文章

  1. 优秀后端架构师必会知识:史上最全MySQL大表优化方案总结

    本文原作者“ manong”,原创发表于segmentfault,原文链接:segmentfault.com/a/1190000006158186 1.引言   MySQL作为开源技术的代表作之一,是 ...

  2. MySQL 大表优化方案(长文)

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  3. 知识点:Mysql 数据库索引优化实战(4)

    知识点:Mysql 索引原理完全手册(1) 知识点:Mysql 索引原理完全手册(2) 知识点:Mysql 索引优化实战(3) 知识点:Mysql 数据库索引优化实战(4) 一:插入订单 业务逻辑:插 ...

  4. 详解MySQL大表优化方案( 转)

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  5. MySQL 大表优化方案探讨

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  6. MySQL大表优化方案

    转:https://segmentfault.com/a/1190000006158186?hmsr=toutiao.io&utm_medium=toutiao.io&utm_sour ...

  7. MySQL 大表优化方案

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  8. MySQL大表优化方案 Mysql的row_format(fixed与dynamic)

    转自:https://mp.weixin.qq.com/s/VY69wWlrVLjRtKU7ULrYGw 当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除 ...

  9. 详解MySQL大表优化方案

    单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型值为主的表在千万级以下,字符串为主的表在五百万以下是没有太大问题的.而事实上很多时 ...

随机推荐

  1. Java实现 LeetCode 752 打开转盘锁(暴力)

    752. 打开转盘锁 你有一个带有四个圆形拨轮的转盘锁.每个拨轮都有10个数字: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' .每个拨轮可以自由旋 ...

  2. Java实现 LeetCode 479 最大回文数乘积

    479. 最大回文数乘积 你需要找到由两个 n 位数的乘积组成的最大回文数. 由于结果会很大,你只需返回最大回文数 mod 1337得到的结果. 示例: 输入: 2 输出: 987 解释: 99 x ...

  3. Java实现 LeetCode 332 重新安排行程

    332. 重新安排行程 给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序.所有这些机票都属于一个从JFK(肯尼迪国际机场 ...

  4. Java实现 LeetCode 81 搜索旋转排序数组 II(二)

    81. 搜索旋转排序数组 II 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] ). 编写一个函数来判 ...

  5. Java实现第八届蓝桥杯承压计算

    承压计算 题目描述 X星球的高科技实验室中整齐地堆放着某批珍贵金属原料. 每块金属原料的外形.尺寸完全一致,但重量不同. 金属材料被严格地堆放成金字塔形. 7 5 8 7 8 8 9 2 7 2 8 ...

  6. Python学习之输入输出入门 A+B篇

    描述 求两个整数之和. 输入 输入数据只包括两个整数A和B. 输出 两个整数的和. 样例输入 1 2 样例输出  3 a=input().split() print(int(a[0])+int(a[1 ...

  7. 服务端监控工具Nmon使用方法

    一.认识nmon 1.简介 nmon是一种在AIX与各种Linux操作系统上广泛使用的监控与分析工具,它能在系统运行过程中实时地捕捉系统资源的使用情况,记录的信息比较全面, 并且能输出结果到文件中,然 ...

  8. ElasticSearch系列之(一):介绍、安装(Docker、Windows、Linux)

    1.介绍 Elasticsearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用Java语言开发的,并 ...

  9. Spring AOP 扫盲

    关于AOP 面向切面编程(Aspect-oriented Programming,俗称AOP)提供了一种面向对象编程(Object-oriented Programming,俗称OOP)的补充,面向对 ...

  10. (一)c++之细解 const 与 static

    const成员变量与const成员函数与const对象 static成员变量与static成员函数与static全局变量 const成员变量 1. const用于类中成员变量时,将类成员变为只读属性( ...