HelloCoder HelloCoder
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
随笔
关于作者
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
随笔
关于作者
  • 《LearnJavaToFindAJob》

    • 导读

    • 【初级】6~12k档

    • 【中级】12k-26k档

      • JVM进阶

      • Java进阶

      • MySQL

        • Innodb和MyISAM索引的区别
        • MySQL一些索引失效的场景和原理
        • MySQL如何行转列?
        • MySQL的架构和执行流程
        • MySQL的自增ID用完了会怎样?
        • MySQL索引的分类、何时使用、何时不使用、何时失效?
        • MySQL联合索引在B+数的存储结构和最左匹配原则原理
        • Mysql的索引和主键的区别
        • binlog、redolog、undolog的区别和作用
        • 什么是前缀索引,什么情况才使用?
        • 可重复读是否能解决幻读?
        • 我以为我对Mysql事务很熟,直到我遇到了阿里面试官
        • 聊聊MySQL索引的分类和结构吧
      • 中间件

      • 算法

      • 高阶

    • 【高级】26k+档

    • 大厂面试题

    • 求职建议

    • 面经

  • LearnJavaToFindAJob
  • 【中级】12k-26k档
  • MySQL
码农阿雨
2022-06-02
目录

什么是前缀索引,什么情况才使用?

什么是前缀索引、什么情况下才使用前缀索引?

我相信很少业务会用到前缀索引,我也很少用到,但是MySQL既然出现了这个概念,说明还是有用的,所以还是得了解一下。

恰好面试有被问到过,就简单记录一下。

# 什么是前缀索引?

前缀索引也叫局部索引,比如身份证一共18位,给前 10 位添加索引,类似这种给某列部分信息添加索引的方式叫做前缀索引。

# 为什么要用前缀索引?

针对blob、text、很长的varchar字段,mysql不支持索引他们的全部长度。

前缀索引能有效减小索引文件的大小,让每个索引页可以保存更多的索引值,从而提高了索引查询的速度。

缺点:

无法应用于 order by 和 group by,也无法做覆盖扫描。

# 什么情况下适合使用前缀索引?

当字符串本身可能比较长,而且前几个字符就开始不相同,适合使用前缀索引;相反情况下不适合使用前缀索引,比如,整个字段的长度为 20,索引选择性为 0.9,而我们对前 10 个字符建立前缀索引其选择性也只有 0.5,那么我们继续加大前缀字符的长度,但是这个时候前缀索引的优势已经不明显,就没有创建前缀索引的必要了。

索引值重复性越低,查询效率也就越高。

所以:

  1. 前缀索引应该足够长,使前缀索引的选择性趋紧于索引整个列的选择性,太短的话就会有大量的索引值重复。
  2. 前缀索引又不能太长,因为要降低索引的大小,减少空间占用

下面再举个例子深入理解一下。

模拟10000条数据:

CREATE TABLE `t_test_qianzhui` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(255) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;


CREATE PROCEDURE `t_test_qianzhui`()
begin
  DECLARE i INT DEFAULT 1;
	WHILE i < 100000 DO
INSERT into t_test_qianzhui(`name`) VALUES (CONCAT('CN.',rand()*1,'.',rand()*100,'.',rand()*1000));
SET i = i+1;
end WHILE;
end;

CALL t_test_qianzhui();

# 如何确定前缀索引的长度?

答案是 完整列覆盖率。

可以使用以下SQL进行估计:

# 1、单条数据的完整列:

select count(distinct `name`) / count(*) from t_test_qianzhui;

计算完整列的数据:

mysql> select count(distinct `name`) / count(*) from t_test_qianzhui;
+-----------------------------------+
| count(distinct `name`) / count(*) |
+-----------------------------------+
|                            1.0000 |
+-----------------------------------+
1 row in set (0.05 sec)

# 2、估算

对每列名进行估算

select count(distinct left(`name`,3))/count(*) as sel3,
count(distinct left(`name`,4))/count(*) as sel4,
count(distinct left(`name`,5))/count(*) as sel5, 
count(distinct left(`name`,6))/count(*) as sel6 ,
count(distinct left(`name`,7))/count(*) as sel7 ,
count(distinct left(`name`,8))/count(*) as sel8 ,
count(distinct left(`name`,9))/count(*) as sel9 ,
count(distinct left(`name`,10))/count(*) as sel10 ,
count(distinct left(`name`,11))/count(*) as sel11 
from t_test_qianzhui;

结果:

mysql> select count(distinct left(`name`,3))/count(*) as sel3,
    -> count(distinct left(`name`,4))/count(*) as sel4,
    -> count(distinct left(`name`,5))/count(*) as sel5,
    -> count(distinct left(`name`,6))/count(*) as sel6 ,
    -> count(distinct left(`name`,7))/count(*) as sel7 ,
    -> count(distinct left(`name`,8))/count(*) as sel8 ,
    -> count(distinct left(`name`,9))/count(*) as sel9 ,
    -> count(distinct left(`name`,10))/count(*) as sel10 ,
    -> count(distinct left(`name`,11))/count(*) as sel11
    -> from t_test_qianzhui;
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| sel3   | sel4   | sel5   | sel6   | sel7   | sel8   | sel9   | sel10  | sel11  |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| 0.0001 | 0.0007 | 0.0066 | 0.0699 | 0.1139 | 0.5537 | 0.9324 | 0.9930 | 0.9991 |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
1 row in set (0.14 sec)

可以看到 sel9 ,也就是取了9个前缀索引的时候,就 0.9324 约等于 1 (distinct name) 了。

这是个数学问题、你细品~

而再长的话,也是可以的,但是这个覆盖率已经很高了,空间和时间的换取,那就看你的业务了。

# 3、添加前缀索引

10w条数据,没有加查询耗时大概是0.145秒。

SELECT SQL_NO_CACHE `name` FROM t_test_qianzhui WHERE name ='CN.0.8491296161423713.1.349709836160494.520.0966741145558';

加上前缀索引:

alter table t_test_qianzhui add key (`name`(9));

查询耗时大概是 0.025秒。

假如前缀索引用的不当:

alter table t_test_qianzhui add key (`name`(5));

查询耗时大概是 0.186秒,反而比不加索引的查询更耗时了。

这和数据量、列名、硬件 有关系,想测试的可以把数据加到100w,会更明显。

综上,前缀索引还是有很大的优势的。

# 后缀索引

既然可以使用前缀索引,那是否可以使用后缀索引呢?

mysql不支持后缀索引的

解决方案:可以在表中添加一个新列,用于保存要被建立后缀索引的字段倒排值,然后建立前缀索引。

例如查邮箱分类的后缀。

阅读全文
×

(为防止恶意爬虫)
扫码或搜索:HelloCoder
发送:290992
即可永久解锁本站全部文章

解锁
上次更新: 2025-02-21 06:04:57
最近更新
01
《LeetCode 101》
02-21
02
IDEA、Golang、Pycharm破解安装
02-21
03
《LeetCode CookBook》
02-21
更多文章>
Theme by Vdoing | Copyright © 2020-2025 码农阿雨
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式