有时候需要索引很长的字符列,这会让索引变得大且慢。通常可以索引开始的部分字符,这样可以大大节约索引空间,从而提高索引效率。但这样也会降低索引的选择性。索引的选择性是指不重复的索引值(也称为基数,cardinality)和数据表的记录总数的比值,范围从1/#T到1之间。索引的选择性越高则查询效率越高,因为选择性高的索引可以让MySQL在查找时过滤掉更多的行。唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

一般情况下某个前缀的选择性也是足够高的,足以满足查询性能。对于BLOB,TEXT,或者很长的VARCHAR类型的列,必须使用前缀索引,因为MySQL不允许索引这些列的完整长度。

诀窍在于要选择足够长的前缀以保证较高的选择性,同时又不能太长(以便节约空间)。前缀应该足够长,以使得前缀索引的选择性接近于索引的整个列。换句话说,前缀的”基数“应该接近于完整的列的”基数“。

为了决定前缀的合适长度,需要找到最常见的值的列表,然后和最常见的前缀列表进行比较。下面的示例是mysql官方提供的示例数据库

下载地址如下:

http://downloads.mysql.com/docs/sakila-db.zip

在示例数据库sakila中并没有合适的例子,所以从表city中生成一个示例表,这样就有足够数据进行演示:

  1. mysql> select database();
  2. +------------+
  3. | database() |
  4. +------------+
  5. | sakila |
  6. +------------+
  7. 1 row in set (0.00 sec)
  8.  
  9. mysql> create table city_demo (city varchar(50) not null);
  10. Query OK, 0 rows affected (0.02 sec)
  11.  
  12. mysql> insert into city_demo (city) select city from city;
  13. Query OK, 600 rows affected (0.08 sec)
  14. Records: 600 Duplicates: 0 Warnings: 0
  15.  
  16. mysql> insert into city_demo (city) select city from city_demo;
  17. Query OK, 600 rows affected (0.07 sec)
  18. Records: 600 Duplicates: 0 Warnings: 0
  19.  
  20. mysql> update city_demo set city = ( select city from city order by rand() limit 1);
  21. Query OK, 1199 rows affected (0.95 sec)
  22. Rows matched: 1200 Changed: 1199 Warnings: 0
  23.  
  24. mysql>

因为这里使用了rand()函数,所以你的数据会与我的不同,当然那不影响聪明的你。

首先找到最常见的城市列表:

  1. mysql> select count(*) as cnt, city from city_demo group by city order by cnt desc limit 10;
  2. +-----+--------------+
  3. | cnt | city |
  4. +-----+--------------+
  5. | 8 | Garden Grove |
  6. | 7 | Escobar |
  7. | 7 | Emeishan |
  8. | 6 | Amroha |
  9. | 6 | Tegal |
  10. | 6 | Lancaster |
  11. | 6 | Jelets |
  12. | 6 | Ambattur |
  13. | 6 | Yingkou |
  14. | 6 | Monclova |
  15. +-----+--------------+
  16. 10 rows in set (0.01 sec)
  17.  
  18. mysql>

注意到查询结果,上面每个值都出现了6-8次。现在查找到频繁出现的城市前缀。先从3个前缀字母开始,然后4个,5个,6个:

  1. mysql> select count(*) as cnt,left(city,3) as pref from city_demo group by pref order by cnt desc limit 10;
  2. +-----+------+
  3. | cnt | pref |
  4. +-----+------+
  5. | 25 | San |
  6. | 15 | Cha |
  7. | 12 | Bat |
  8. | 12 | Tan |
  9. | 11 | al- |
  10. | 11 | Gar |
  11. | 11 | Yin |
  12. | 10 | Kan |
  13. | 10 | Sou |
  14. | 10 | Bra |
  15. +-----+------+
  16. 10 rows in set (0.00 sec)
  17.  
  18. mysql> select count(*) as cnt,left(city,4) as pref from city_demo group by pref order by cnt desc limit 10;
  19. +-----+------+
  20. | cnt | pref |
  21. +-----+------+
  22. | 12 | San |
  23. | 10 | Sout |
  24. | 8 | Chan |
  25. | 8 | Sant |
  26. | 8 | Gard |
  27. | 7 | Emei |
  28. | 7 | Esco |
  29. | 6 | Ying |
  30. | 6 | Amro |
  31. | 6 | Lanc |
  32. +-----+------+
  33. 10 rows in set (0.01 sec)
  34.  
  35. mysql> select count(*) as cnt,left(city,5) as pref from city_demo group by pref order by cnt desc limit 10;
  36. +-----+-------+
  37. | cnt | pref |
  38. +-----+-------+
  39. | 10 | South |
  40. | 8 | Garde |
  41. | 7 | Emeis |
  42. | 7 | Escob |
  43. | 6 | Amroh |
  44. | 6 | Yingk |
  45. | 6 | Moncl |
  46. | 6 | Lanca |
  47. | 6 | Jelet |
  48. | 6 | Tegal |
  49. +-----+-------+
  50. 10 rows in set (0.01 sec)
  1. mysql> select count(*) as cnt,left(city,6) as pref from city_demo group by pref order by cnt desc limit 10;
  2. +-----+--------+
  3. | cnt | pref |
  4. +-----+--------+
  5. | 8 | Garden |
  6. | 7 | Emeish |
  7. | 7 | Escoba |
  8. | 6 | Amroha |
  9. | 6 | Yingko |
  10. | 6 | Lancas |
  11. | 6 | Jelets |
  12. | 6 | Tegal |
  13. | 6 | Monclo |
  14. | 6 | Ambatt |
  15. +-----+--------+
  16. 10 rows in set (0.00 sec)
  17.  
  18. mysql>

通过上面改变不同前缀长度发现,当前缀长度为6时,这个前缀的选择性就接近完整咧的选择性了。甚至是一样的。

当然还有另外更方便的方法,那就是计算完整列的选择性,并使其前缀的选择性接近于完整列的选择性。下面显示如何计算完整列的选择性:

  1. mysql> select count(distinct city) / count(*) from city_demo;
  2. +---------------------------------+
  3. | count(distinct city) / count(*) |
  4. +---------------------------------+
  5. | 0.4283 |
  6. +---------------------------------+
  7. 1 row in set (0.05 sec)
  8.  
  9. mysql>

可以在一个查询中针对不同前缀长度的选择性进行计算,这对于大表非常有用,下面给出如何在同一个查询中计算不同前缀长度的选择性:

  1. mysql> select count(distinct left(city,3))/count(*) as sel3,
  2. -> count(distinct left(city,4))/count(*) as sel4,
  3. -> count(distinct left(city,5))/count(*) as sel5,
  4. -> count(distinct left(city,6))/count(*) as sel6
  5. -> from city_demo;
  6. +--------+--------+--------+--------+
  7. | sel3 | sel4 | sel5 | sel6 |
  8. +--------+--------+--------+--------+
  9. | 0.3367 | 0.4075 | 0.4208 | 0.4267 |
  10. +--------+--------+--------+--------+
  11. 1 row in set (0.01 sec)
  12.  
  13. mysql>

可以看见当索引前缀为6时的基数是0.4267,已经接近完整列选择性0.4283。

在上面的示例中,已经找到了合适的前缀长度,下面创建前缀索引:

  1. mysql> alter table city_demo add key (city(6));
  2. Query OK, 0 rows affected (0.19 sec)
  3. Records: 0 Duplicates: 0 Warnings: 0
  4.  
  5. mysql>
  1. mysql> explain select * from city_demo where city like 'Jinch%';
  2. +----+-------------+-----------+-------+---------------+------+---------+------+------+-------------+
  3. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
  4. +----+-------------+-----------+-------+---------------+------+---------+------+------+-------------+
  5. | 1 | SIMPLE | city_demo | range | city | city | 20 | NULL | 2 | Using where |
  6. +----+-------------+-----------+-------+---------------+------+---------+------+------+-------------+
  7. 1 row in set (0.00 sec)
  8.  
  9. mysql>

可以看见正确使用刚创建的索引。

前缀索引是一种能使索引更小,更快的有效办法,但另一方面也有其缺点:

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

参考资料

<<高性能MySQL第三版>>

MySQL前缀索引和索引选择性的更多相关文章

  1. MySQL索引之前缀索引和索引选择性

    有时需要索引很长的字符列,它会使索引变大而且变慢.一个策略就是模拟哈希索引.但是有时这也不够好,那? 通常可以索引开始的几个字符,而不是全部值,以节约空间并得到好的性能.这使索引需要的空间变小,但是也 ...

  2. mysql索引之四:复合索引之最左前缀原理,索引选择性,索引优化策略之前缀索引

    高效使用索引的首要条件是知道什么样的查询会使用到索引,这个问题和B+Tree中的“最左前缀原理”有关,下面通过例子说明最左前缀原理. 一.最左前缀索引 这里先说一下联合索引的概念.MySQL中的索引可 ...

  3. Mysql如何创建短索引(前缀索引)

    Mysql如何创建短索引 为什么要用短索引 有时需要索引很长的字符列,它会使索引变大并且变慢.一个策略就是模拟哈希索引.但是有时这也不够好,那么应该怎么办呢?通常可以索引开始的几个字符,而不是全部值, ...

  4. mysql优化-----多列索引的左前缀规则

    索引优化策略 :索引类型 .1B-tree索引 关注的是:Btree索引的左前缀匹配规则,索引在排序和分组上发挥的作用. 注:名叫btree索引,大的方面看都用的二叉树.平衡树.但具体的实现上,各引擎 ...

  5. MySQL数据库中的索引(二)——索引的使用,最左前缀原则

    上文中,我们了解了MySQL不同引擎下索引的实现原理,在本文我们将继续探讨一下索引的使用以及优化. 创建索引可以大大提高系统的性能. 第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性. ...

  6. MySQL索引 索引分类 最左前缀原则 覆盖索引 索引下推 联合索引顺序

    MySQL索引 索引分类 最左前缀原则 覆盖索引 索引下推 联合索引顺序   What's Index ? 索引就是帮助RDBMS高效获取数据的数据结构. 索引可以让我们避免一行一行进行全表扫描.它的 ...

  7. mysql前缀索引的应用

    在mysql中有时需要索引的列很长,如果直接应用索引会造成索引过大的问题.因此我们可以取其中一部分字段来做索引,例: 添加索引:alter table * add key (field(3));   ...

  8. mysql 索引及索引创建原则

    是什么 索引用于快速的查询某些特殊列的某些行.如果没有索引, MySQL 必须从第一行开始,然后通过搜索整个表来查询有关的行.表越大,查询的成本越大.如果表有了索引的话,那么 MySQL 可以很快的确 ...

  9. 高性能mysql 第五章 索引部分总结

    高性能索引 1.索引基础:索引的作用类似'目录'帮助Query来快速定位数据行. 1.1索引类型: 1.1.1 b-tree索引 b-tree(balance tree)索引:使用平衡树(非平衡二叉树 ...

随机推荐

  1. [Laravel] 01 - Love beautiful code? We do too.

    前言 一.良心资料 英文 Laravel 框架:https://laravel.com/ 教程:https://laracasts.com/series/ laravel-from-scratch-2 ...

  2. Apache Nginx URL 地址 重写

    URL重写这东西在工作中用了很多次了,但每次都忘记了要记得把知道的积累下来. 哎,要么认为没必要,要么就是没时间?! 一.Apache 篇 官方地址:http://man.chinaunix.net/ ...

  3. 【netcore基础】.NET Core使用EPPlus实现MVC API里的Excel导出功能 配置中文表头

    EPPlus 用来操作excel非常方便,不用依赖微软的office包,所以推荐使用. 下面是具体步骤和代码 首先用nuget安装 EPPlus.Core 我装的版本是 1.5.4 然后就可以用 Ex ...

  4. VS 2013 未找到与约束contractname Microsoft.VisualStudio.Utilities.IContentTypeRegistryService......匹配的导出

    错误信息: 点击解决方案中的文件,就会提示这个错误.最近给vs2013安装了python的插件,安装了vs2017的python,可能是这两个导致了这个错误. 解决方案: 删除 C:\Users\ji ...

  5. D - Area of Mushroom

    Teacher Mai has a kingdom with the infinite area. He has n students guarding the kingdom. The i-th s ...

  6. linux 下 sublime配置

    sublime3 import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_p ...

  7. day4 四、流程控制之if判断、while循环、for循环

    一.if判断 1.语法一: if 条件: 条件成立时执行的子代码块 代码1 代码2 代码3 示例: sex='female' age= is_beautiful=True and age < a ...

  8. db2 基础语法

    一.db2 基础 基本语法 注释:“--”(两个减号) 字符串连接:“||” 如set msg=’aaaa’||’bbbb’,则msg为’aaaabbbb’ 字符串的引用:‘’(一定用单引号),如果需 ...

  9. .NET Core开发日志——Middleware

    熟悉ASP.NET架构的开发者一定对于HTTP Modules与HTTP Handlers不陌生.两者的作用主要是对网络请求执行特定的处理工作.而在.NET Core中,它们都被Middleware( ...

  10. Eonasdan bootstrap datetimepicker 使用记录

    开始用的 bootstrap日期选择控件是 bootstrap-datepicker : $('#visit_date').datepicker({ todayHighlight: true, sta ...