MySQL索引的优化:高性能索引策略。

文章目录

  • 1 独立的列
  • 2 前缀索引和索引的选择性
  • 3使用联合索引(多列索引)
  • 4联合索引的排列问题

下面是索引使用的一些优化的方法。

1 独立的列

如果我们使用不恰当的检索方式,会使的MySQL无法使用索引。
如:

select  id from student where id+1=5;

前置条件:id为索引列。在这条查询中MySQL没有使用到索引。因为where后面是表达式:id+1=5;这个不是独立的列,MySQL无法直接解析这个表达式,所以无法使用到索引为id的列。

2 前缀索引和索引的选择性

假设的索引是很长的字符列,那么我们的索引就会有很大的存储成本而且很慢。所以我们就需要使用前缀索引。
所谓前缀索引,我们通常可以以索引开始的部分字符作为索引,这样可以大大节约索引的空间。但是也会也会降低索引的选择性

索引的选择性=(不重复索引的值)/(总记录数的比值)。
我们需要知道的是,索引的选择性越高,那么查询的效率就越高。
诀窍在于要选择足够长的前缀以保证较高的选择性,同时又不能太长(节省空间)。
那么问题来了,如何计算索引的选择性,如果没有例子,那么上面一堆概念将是废话。
方法一:
计算总的数据量:

select count(*) as cnt,city from sakila.city_demo
 ground by city cnt desc limit 10;

如果对SQL语句不了解,解释一下:count(*)计算数据表记录的总数,和group by 联合起来使用,就是根据city分组,每个city出现的次数。order by desc 就是排列顺序是降序且限制10行。

cnt city
65 London
49 Hiroshima
48 Teboksary
48 Pak Kret
48 Yaound
47 Tel Aviv-Jaffa
47 Shimoga
45 Cabuyao
45 Callao
45 Bislig

我们注意到:上面的每个值都出现了45~65次。现在查找最频繁出现的城市前缀,先从3个字母前缀开始。

select count(*) as cnt,left(city,3) as pref from sakila.city_demo
 group by pref order by cnt desc limit 10;

解释一下:left(被截取的字符串,截取长度),as后为列的别名。

cnt pref
483 San
195 Cha
177 Tan
167 Sou
163 al-
163 Sal
146 Shi
136 Hal
130 Val
129 Bat

可以看到,当截取字符串长度为3时,符合条件的过多,我们需要增大截取的长度。直到这个前缀的选择性接近完整列的选择性。
经过实验我们发现当截取长度为7也就是前缀为7时比较合适

select count(*) as cnt,left(city,7) as pref from sakila.city_demo
group by pref order by cnt desc limit 10;
cnt pref
70 Santiag
68 San Fel
65 London
61 Valle d
49 Hiroshi
48 Teboksa
48 Pak Kre
48 Yaound
47 Tel Avi
47 Shimoga

不过这样方法还是有点麻烦,下面是第二种方法。
我们计算完整列的选择性,并使前缀的选择性接近于完整列的选择性。

select count(distinct city)/count(*) from sakila.city_demo;
count(distinct city)/count(*)
0.0312

如果比值接近0.0312,那么基本上就够了
下面为实例:

select count(distinct left(city,3))/count(*) as sel3
select count(distinct left(city,4))/count(*) as sel4
select count(distinct left(city,5))/count(*) as sel5
select count(distinct left(city,6))/count(*) as sel6
select count(distinct left(city,7))/count(*) as sel7
from sakila.city_demo;
sel3 sel4 sel5 sel6 sel7
0.0239 0.0293 0.0305 0.0309 0.0310

那么当到7的时候,就已经够了,再增加一个,增加的幅度也不大。所以选7.
前缀索引的缺点:MySQL无法使用前缀索引做order by 和group by,也无法使用前缀索引做扫描。

下面展示如何创建前缀索引:

alter table sakila.city_demo add key (city(7));

3使用联合索引(多列索引)

首先我们创建单列索引来测试:

 create table t(t1 int ,t2 int ,t3 int,key(t1),key(t2),key(t3));
 insert into t(t1,t2,t3)values(1,2,3),(2,3,4);

那么当我们使用这种条件查询的时候,是否会使用到索引呢?
答案是不。
MySQL索引的优化:高性能索引策略。_第1张图片使用explain关键字的type字段,发现我们使用的是all
这个时候,我们就需要使用联合索引(复合索引、多列索引)。
使用场景:
在MySQL5.0及以后的版本中,查询条件为or、add或者而二者的联合,我们就可以使用复合索引。
查看案例:

 create table t2(t1 int ,t2 int ,t3 int,key(t1,t2);
 insert into t2(t1,t2,t3)values(1,2,3),(2,3,4);

MySQL索引的优化:高性能索引策略。_第2张图片在这里我就已经使用了我们的索引。(type:index)

联合索引 key(姓,名, 生日)
对于联合索引,需要注意的是:
1最左匹配原则:如果不是从索引的的最左侧开始找,那么这个索引将会失效。
在上面这个联合索引中,无法用于查找名字为Bill的人,也无法查找某个特定日期生日的人,因为这两列都不是最左序列(最左序列是姓),同样也无法
2.如果查询中有某个列的范围查询,那么其右边所有的列都无法使用索引的优化查询。
例如:where 姓 =’Smith’ AND 名 LIKE ‘J%’ AND dob=’1976-12-23’.
这个查询只能查索引的前两列,因为这里LIKE是一个范围条件。

3.不能跳过索引的列。比如查找条件是姓和生日,那么MySQL索引只能用索引的第一列。

4联合索引的排列问题

现在有个问题:

select * from payment where staff_id=2 and customer_id=584

那么我们需要创建一个(staff_id,customer_id)的索引还是应该颠倒一下顺序。
注意这里的索引是指B+树为数据结构的索引,不是哈希索引。
索引的排列顺序对于查询的效率至关重要。
对于经验而言,我们考虑全局的基数和选择性。则有:

select count(distinct staff_id)/count(*) as staff_id_selectivity,
count(distinct customer_id)/count(*) as customer_id_selectivity,
count(*)
from payment;
staff_id_selectivity:0.0001
customer_id_selectivity:0.0373
count(*):16049

customer_id的选择性更高,所以讲它作为第一列。

alter table payment add key(customer_id,staff_id);

参考:《高性能MySQL:高性能索引的策略》

你可能感兴趣的:(mysql)