MySql 你真的会使用字符串索引吗???

志在峰巅的攀登者,不会陶醉在沿途的某个脚印之中,在码农的世界里,优美的应用体验,来源于程序员对细节的处理以及自我要求的境界,年轻人也是忙忙碌碌的码农中一员,每天、每周,都会留下一些脚印,就是这些创作的内容,有一种执着,就是不知为什么,如果你迷茫,不妨来瞅瞅码农的轨迹。

我们这里有一张用户表,建表语句如下

CREATE TABLE `t_user`(
	`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID' ,
	`id_card` VARCHAR(32) DEFAULT NULL COMMENT '用户身份证' ,
	`u_name` VARCHAR(32) DEFAULT NULL COMMENT '用户姓名' ,
	`u_phone` VARCHAR(32) DEFAULT NULL COMMENT '用户电话' ,
	`u_password` VARCHAR(200) DEFAULT NULL COMMENT '用户密码' ,
	`u_age` INT(11) DEFAULT NULL COMMENT '用户年龄' ,
	`u_male` TINYINT(1) DEFAULT NULL ,
	PRIMARY KEY(`id`) ,
	KEY `id_card`(`id_card`) ,
	KEY `u_phone`(`u_phone`) 
) ENGINE = INNODB

这个表中有三个索引 id 的主键索引、id_card 的身份索引、手机号的索引。

1 字符串 前缀索引

我们在实际业务开发中,会有手机号密码登录的功能,所以会常有查询语句如下:

select * from t_user where u_phone='xxx' and u_password='xxx';

所以我们在 u_phone 上添加了索引,如果不添加索引,这个查询就会走全表扫描,当用户量足够大时,这个查询会足够的慢。


在这里我们为 u_phone 添加的索引,默认是11位长度的(因为手机号一般是11位),MySQL 是支持前缀索引的,所以可以考虑定义字符串的一部分作为索引,如这里我们把 u_phone 原来 11位的长度索引修改为 4 位长度的索引:

#先删除索引
ALTER TABLE t_user DROP INDEX u_phone;
#再修改
alter table t_user add index u_phone2(u_phone(4));

相比于之前的索引,后者 u_phone2 索引结构中每个电话号码字段都只取前 64 个字节,占用的空间会更小,查询速度会更快,但这可能会增加额外的记录扫描次数。

2 普通索引与前缀索引 查询过程分析

如执行以下查询语句

select * from t_user where u_phone='13309090909'

如果我们添加的是普通索引如下:

alter table t_user add index u_phone('u_phone');

那么在执行查询过程:

  1. 从 u_phone 索引树找到满足索引值是’13309090909’的这条记录对应的ID
  2. 然后回表到主键索引上 获取对应ID的这一条数据
  3. 然后在 u_phone 索引树取刚刚查到的位置的下一条记录,判断不浪花兄弟条件,循环结束。

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

在这里插入图片描述

如果我们使用的是前缀索引,如下

alter table t_user add index u_phone2(u_phone(4));

那么在执行查询过程:

  1. 从 u_phone2 索引树找到满足条件的第一个记录ID;
  2. 然后回表到主键上查询获取这一行数据,然后判断出 这条数据中的电话号码是 ‘13309090909’,满足条件,保留数据,否则丢弃这条查询
  3. 然后再去 u_phone2 索引树找到满足条件 ‘1330’ 的ID,然后回表到ID 索引上取整行然后判断,这次值对了,将这行记录加入结果集;
  4. 重复上一步,直到在 u_phone2 上取到的值不是’1330’时,循环结束。

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

在这里插入图片描述
当我们再取 u_phone 字段的前5个字节或者是前6个字节来构建索引,查询次数又可能不一样,也就是使用前缀索引,定义好长度,就可以做到既节省空间,又不用额外增加太多的查询 成本。

关键就是定义前缀索引的长度的权衡问题

3 前缀索引的长度的权衡

我们可以先查询这个表中不同手机号码的用户数量

select count(distinct mobile) as L from tb_user;

比如我这个测试表中的查询结果如下:

在这里插入图片描述

然后,依次选取不同长度的前缀来看这个值对比一下不同数据的数量:

SELECT
	count(DISTINCT LEFT(mobile , 4)) AS L4 ,
	count(DISTINCT LEFT(mobile , 5)) AS L5 ,
	count(DISTINCT LEFT(mobile , 6)) AS L6 ,
	count(DISTINCT LEFT(mobile , 7)) AS L7
FROM
	tb_user

在这里插入图片描述

对比一下,在这里我们可以使用 前缀7个字节,这样区别扫描次数最少,如我们这个表中有 190 个不同的电话号码用户数据,如果使用电话号码的全字段创建索引,有190个不同的值,在查询数据里,扫描电话号码索引表查询到数据,扫描一次。

如果选用前7个字节构建索引数据结构,重复的索引值有 190-124 = 56个,也就是说当查询到前缀正好在这56个重复索引中,需要回表查询56次进行判断。

如果选用前6个字节构建索引数据结构,重复的索引值有 190-78 = 112个,也就是说当查询到前缀正好在这112个重复索引中,需要回表查询112次进行判断。

所以需要选择 前缀7个字节。


完毕

不局限于思维,不局限语言限制,才是编程的最高境界。

以小编的性格,肯定是要录制一套视频的,随后会上传

有兴趣 你可以关注一下 西瓜视频 — 早起的年轻人

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

早起的年轻人

创作源于分享

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值