005 mysql之如何创建高性能索引


前言:知识点大部分来自于《高性能MySQL》,大家有空的话可以知己去看原著第五章。

一.索引基础

索引类型:大致有四种。

a.B-free索引

B-Tree所有的值都是按顺序来存储的,并且每一个叶子页到根的距离相同。InnoDB的B+Tree的如图:在这里插入图片描述
B-Tree是按照顺序来存储的,所以很适合查找范围数据。在这里插入图片描述
B-Tree对如下的查询有效:

  • 全值匹配:和索引中所有列进行匹配,例如前面提到的所以可用于查询姓名为CuBaAllen,出生日期为1960-01-01的人。
  • 最左匹配:可用于查找所有姓为Allen的人,即只使用到索引的第一列。
  • 匹配列前缀:可以只匹配某一列的开头部分。
  • 匹配范围值:可用于查找在Allen和Barrymore之间的人。
  • 精确匹配某一列并范围匹配另一个列:
  • 只访问索引:即查询只需要访问索引,无须访问数据行,即索引覆盖。
  • 查询还可以用在ORDER BY操作,前提是ORDER BY子句满足前面列出的集中查询类型。

B-Tree索引的限制:

  • 必须是最左匹配
  • 不能跳过索引中的列(联合索引):例如联合索引(A,B,C),不能只用B来索引
  • 范围查询某个列时,则其右边的列都无法使用索引

b.哈希索引

基于哈希表实现,只有精确匹配索引所有列。对于每一行数据,存储引擎都会对所有索引列计算一个哈希码,哈希码是一个较小的值,不同的键值算出的哈希码不一样。哈希索引将所有的哈希码存储在索引中,同时哈希表中保存每个数据行的指针。因为索引自身只需要存储对应的哈希值,所以索引十分紧凑,所以哈希索引查找非常快。
限制

  • 哈希索引只保存哈希码和行指针,不能只用索引覆盖。
  • 无法用于排序。
  • 无法支持部分匹配。
  • 只支持等值比较,包括=,IN(),<=>,不支持范围查询。
  • 哈希码可能有冲突,此时行指针指向一个链表,当出现冲突时,存储引擎必须遍历链表的所有行进行比较。
  • 哈希冲突很多的话,一些索引维护代价很会很高

创建自定义的哈希索引:在B-Tree上创建一个伪哈希索引,比如现在要存储一个URL,如果将URL当做索引,索引会很大,所以我们可以将URL计算成一个哈希值,将哈希值当做B-Tree的索引,可以使用CRC32做哈希。在这里插入图片描述

c.空间索引

MyISAM支持空间索引。和B-Tree不同,这类索引无须前缀查询。空间索引会从所有维度来索引数据。

d.全文索引

全文索引是一种特殊索引。是查找列的关键字,而不是比较索引中值,全文索引和其他索引匹配方式完全不一样,如停用词,词干,复数,布尔搜索等。全文搜索更类似搜索引擎做的事,而不是简单的where,在相同的列上创建全文索引和基于值得B-Tree不会冲突,全文索引适用于match against操作。

二.索引优点

最常见的是B-Tree索引,所有的值都是顺序存储,所以可以用来做ORDER BY 和 GROUP BY等操作。因为B-Tree存储实际的列值,所以某些查询只需要索引上的值就够了,不需要回表查询,速度非常快。
总结下来索引有三个优点:

  • 索引大大减小了服务器需要扫描的数据量。
  • 索引可以帮助服务器避免排序和临时表。
  • 索引可以将随机IO变为顺序IO

这里引进一个三星索引的概念,来自于《数据库索引的设计与优化》
三星索引:

  • 索引将相关的记录放到一起(一星)
  • 如果索引中的数据顺序和查找的排序顺序一致(一星)
  • 如果索引中的列包含了查询中需要的全部列(一星)

三.高性能的索引策略

这里主要讲B+free索引

1.独立的列

索引列不能是表达式的一部分,例如下面的两个查询就用不到索引

select * from patients where age + 1 = 100
select * from patients where left(createtime, 10) = '2018-12-25'

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

对于BLOB和TEXT以及很大的VARCHAR,MySQL不允许将整个值当做索引,所以可以将值的前缀部分当做索引。
选择适应长度的前缀很关键。可以如下做参考:

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

查询结果:在这里插入图片描述
从结果我们可以看出,当前缀长度为11时已经很接近全值作为索引的选择性了。
但是前缀索引有一个缺点:无法用索引来ORDER BY和GROUP BY,也无法使用索引覆盖。
与前缀索引相对于的是后缀索引,但是MySQL原生不支持后缀索引,可以将字符串反转来存储,然后再做成前缀索引。
这里我们举个例子:身份证一般是前面是省区信息,后面才是个人信息。这时候如果前缀索引的话,选择性就会很小。这时候如果拿后面机会来做索引,选择性就会很大,你见过几个身份证后面和你一样的人。这时候应该倒叙存储,然后拿前几位来做索引。

3.多列索引

为每个列单独创建索引,例如:

// name和doctorid都单独建了索引
select * from patients where name = '方%' or doctorid = 477;

在老版本MySQL中会全表扫描,而在MySQL5.0及以上,查询可以使用两个单列索引扫描,然后将结果合并。
这个索引合并算法有三个变种:

  • OR的联合(union)
  • AND的相交(intersection)
  • 联合及相交

索引合并策略是一种优化策略,但是也说明表的索引建得很糟糕。在这里插入图片描述

4.选择合适的索引顺序

在不需要考虑排序和分组的情况下,选择性高的列优先在前面,但是也要结合实际情况。
现在要将diseaseid和doctorid做联合索引

select
  count(distinct diseaseid) / count(*) as disease,
  count(distinct doctorid) / count(*) as doctor
from patients;

结果:
在这里插入图片描述
doctorid的选择性明显比diseaseid高很多,这时候联合索引的顺序应该是:(doctorid,diseaseid)。

5.索引覆盖

当发起一个被索引覆盖的查询,在EXPLAIN的Extra列可以看到Using index,例如patients上有doctorid索引:select doctorid from patients;时就会索引覆盖,速度非常快,直接从索引上获取数据,不需要回表查。

explain select doctorid
from patients
where doctorid in (477,33);

如果Explain中的Extra出现了Using index则表示索引覆盖,不需要回表查询数据。
在这里插入图片描述
在这里插入图片描述

6.使用索引扫描来排序

MySQL有两种方式生成有序的结果:排序;按索引顺序扫描;
如果EXPLAIN出来的type列的值为“index”,这说明MySQL使用了索引来排序。

explain select doctorid from patients

在这里插入图片描述

排序没用到索引:

explain select * from patients order by doctorid, diseaseid

在这里插入图片描述
排序用到索引:

alter table patients add index idx_doctorid_diseaseid (doctorid,diseaseid);
explain select * from patients order by doctorid, diseaseid limit 100;

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

7.冗余和重复索引

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值