Mysql索引

  有时候需要索引很长的字符列,如 BLOB、TEXT 或者很长的 VARCHAR 类型的列,这会让索引变得很大,导致查询很慢。对于这种情况,我们可以使用前缀索引来索引开始的部分字符,这样可以大大的节约索引空间,从而提高索引效率。但这样也会降低索引的选择性。

  索引的选择性是指:不重复的索引值(也称为基数,cardinality)和数据表的记录总数(#T)的比值,范围从1/#T到1之间。

  索引的选择性越高则查询效率越高,因为选择性高的索引可以过滤更多的行。唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。如下示例:

1 1 // 创建表 2 2 CREATE TABLE sakila.city_demo( 3 3 city VARCHAR(50) NOT NULL 4 4 ); 5 5 6 6 // 自我复制 7 7 INSERT INTO sakila.city_demo(city) SELECT `city` FROM sakila.city; 8 8 9 9 // 从城市表中随机赋值数据到城市测试表 1010 UPDATE sakila.city_demo SET city = (SELECT city FROM sakila.city ORDER BY RAND() LIMIT 1);

现在我们已经有了一个测试数据集,现在,我们来统计城市表中,最常见的城市:

1 1 SELECT 2 2 COUNT(*) AS cnt, 3 3 city 4 4 FROM 5 5 sakila.city_demo 6 6 GROUP BY 7 7 city 8 8 ORDER BY 9 9 cnt DESC 1010 LIMIT 10;

结果集如下:

以上的10个城市就是最频繁出现的城市前缀,现在先从3个前缀字母开始:

1 1 SELECT 2 2 COUNT(*) AS cnt , 3 3 LEFT(city, 3) AS pref 4 4 FROM 5 5 sakila.city_demo 6 6 GROUP BY 7 7 pref 8 8 ORDER BY 9 9 cnt DESC 1010 LIMIT 10;

结果集如下:

 由上结果可知,每个前缀都比原来的城市出现的次数更多,因此唯一前缀比唯一城市要少得多。然后我们继续增加长度测试,直到这个前缀的选择性接近完整列的选择性。最后,我们发现,当长度为7时,最适合:

计算合适的前缀长度的另外一个方法就是计算完整性的选择性,并使前缀的选择性接近于完整列的选择性。下面显示如何计算完整列的选择性:

11 SELECT 22 COUNT(DISTINCT city)/COUNT(*) 33 FROM 44 sakila.city_demo;

结果集为:

通常来说,如果前缀的选择性能够接近0.031,基本已经可以用了。当然,也可以在一个查询中,针对不同前缀长度进行计算,这对于大表非常有用。

11 SELECT 22 COUNT(DISTINCT LEFT(city, 3))/COUNT(*) AS sel3, 33 COUNT(DISTINCT LEFT(city, 4))/COUNT(*) AS sel4, 44 COUNT(DISTINCT LEFT(city, 5))/COUNT(*) AS sel5, 55 COUNT(DISTINCT LEFT(city, 6))/COUNT(*) AS sel6, 66 COUNT(DISTINCT LEFT(city, 7))/COUNT(*) AS sel7, 77 FROM 88 sakila.city_demo;

结果集如下:

查询显示当前缀索引长度到达7的时候,再增加前缀长度,选择性提升的幅度已经很小了,处于索引长度越长,索引越大,查询越慢的考虑,所以长度为7是比较适合的。

当然,只看平均选择性是不够的,也有例外的情况。根据上面的平均选择性来看,你可能会认为前缀长度为4或者5的索引已经足够了,但如果数据分布很不均匀,可能会存在陷阱,现在我们来观察前缀长度为4的最长出现城市的次数:

由上可知,如果前缀是4个字节,则最常出现的前缀的出现次数比最常出现的城市的出现次数要大很多。即这些值的选择性比平均选择性要低。

下面我们来演示如何创建前缀索引:

ALTER TABLE sakila.city_demo ADD KEY (city(7));

优点:

  能使索引更小、更快。

缺点:

  无法使用前缀索引进行ORDER BY 和 GROUP BY ,也无法使用前缀索引做覆盖扫描。

常见的应用场景:

  针对很长的十六进制唯一ID使用前缀索引。

参考资料:

  高性能MySQL(第3版)

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

distinct效率更高还是group by效率更高?

目录00结论01distinct的使用02groupby的使用03distinct和groupby原理04推荐groupby的原因00结论先说大致的结论(完整结论在文末):在语义相同,有索引的情况下groupby和distinct都能使用索引,效率相同。在语义相同,无索引的情况下:distinct效率高于groupby。原因是di

什么是索引?Mysql目前主要的几种索引类型

一、索引MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。索引分单列索引和组合索引。单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索引包含多个列。创

MySQL千万级别优化·中

MySQL千万级别的查询优化手段·中单列索引(假设在v\_record表中存在id列的索引)1、WHERE条件使用​EXPLAINSELECT\FROMv\_recordWHEREid2​结论:利用索引进行回表查询2、SELECT字段使用