MySQL听讲(三)——索引

索引前言

索引,简单来说,就是数据库的目录。目的是为了提高查询效率。
索引依赖于数据结构,所以每一种索引除了自身的优缺点外,还有该数据结构的优缺点:

数据结构适用场景使用场景时间复杂度缺点
hash索引等值查询O(1)不适合范围查询
有序数组静态存储引擎二分查找O(log(N))插入慢
N叉树快速查找和写入O(log(N))N偏小磁盘IO频繁

ps:
N越小,树越高。每个叶子节点存在一个数据块中,每个块通过链式链接。树高多少,就要一路照下来多少个块,从根到叶。所以树高一般为磁盘IO访问次数。N可以通过page大小来间接控制(5.7(+))。
树的树根的数据块总是在内存中,其第二层节点也有很大概率在内存中。其叶节点在磁盘中。

InnoDB索引模型

InnoDB索引采用B+Tree数据结构,即B+Tree索引模型。

//TODO 缺少一张B+Tree的图。

主键索引(也叫聚簇索引)的叶子节点存储的是整行数据
非主键索引(也叫二级索引)的叶子节点存储的是主键的值
回表:根据非主键索引搜索完毕,又根据主键索引进行查询。如select * from T where name=’xxx’,假设在name上索引,在根据name查询在name的非主键索引上进行查询得到对应行的ID后,然后根据这些ID在主键索引上进行查询。

所以,在查询时,尽量以主键为条件进行查询。

索引维护

页分裂

页合并

主键索引

在建表时,一般都会让带有自增主键。根据上面页分裂和页合并的过程,因为自增主键都是在后面追加,不会对前面的数据有变动,不会触发叶子节点的分裂。而有业务逻辑的字段做主键是无序的,会触发叶子节点的分裂。
主键索引还要考虑每个索引里的值长度大小。比如存1,2,3…等自增数字比身份证号码就占用的空间小的很多。使用整形(int,4个字节)就比长整型(bigint,8个字节)就小得多。
同时,主键也会存储到其他非主键索引中。

综上所述,主键索引的建立有以下要求:

1. 主键优先采用自增主键;
2. 主键类型越小越好。

业务字段适合做主键的场景(K-V场景):

  • 只有一个索引;
  • 该索引必须是唯一索引。

联合索引

覆盖索引
设有联合索引index_a(name,age),在根据name获取所有信息时,先去该索引根据name来获取主键id,然后根据主键id来获取该数据。这种通过非主键索引查询后再通过主键索引查询信息的过程,叫做回表。在根据name获取age时,则会直接在该索引上通过name获取age并返回,不会去走主键索引,针对于这种情况的索引叫做覆盖索引

最左前缀原则
设有联合索引index_a(name,age),在针对于name的精确查询或者name like '张%'的情况,都还是会走该索引的。
因为name在最左边,所以单独使用name的精确查询是生效的。基于这一点,name like ‘张%’这种左边确定的查询也是生效的。
在建立联合索引时,索引内的字段的顺序是很重要的。

ps:如果是联合索引index_b(a,b,c),从左向右,有哪个字段哪个字段生效,如果中间的字段没有,那么从该字段向后的所有字段都不生效,只生效前面的字段。比如where a=1 and c=1,此时只有a生效。

索引下推
该优化是MySQL5.6加入的。
设有联合索引index_a(name,age),在where name like ‘王%’ and age>20时,会在使用索引index_a的时候,进行判断age>20(以前是只做name like ‘王%’),减少回表次数。

索引与排序
设有联合索引index_a(name,age),在where name like ‘王%’ order by age会走索引。
在where条件上的数据量很多时,即便有索引页不会使用的。因为MySQL认为此时全表扫描更快。

普通索引和唯一索引

在了解其区别前,需要了解到数据库成本最高的开销之一就有将数据读到磁盘中

读数据

普通索引和唯一索引的在读数据时的差距是很小的:

  • 普通索引在取数据时,找到这一条数据,然后指针判断下一条是否符合,不符合即返回结果;
  • 唯一索引在取数据时,取到一条数据时,立即返回。

因为InnoDB是按照页来存取的,偶尔会发生下一条在另外一个页上的情况。一页是16K,对于整形字段,一页可以放近千个值。

写数据

普通索引和唯一索引在写数据时的差距是很大的:
这时分两种情况:要修改的数据页是否在内存中。
在内存中:

  • 对于普通索引,找到数据,修改内存页中的数据,END;
  • 对于唯一索引,找到数据,判断没有冲突后修改数据,END;

不在内存中:

  • 对于普通索引,将修改记录保存到change Buffer中,END;
  • 对于唯一索引,从磁盘读取数据,找到数据,判断没有冲突后修改数据,END;

change buffer

该功能只针对于 普通索引 有效。

含义

在不影响数据库一致性的前提下,如果数据页不在内存中,将数据写入到change buffer中,在下次读取这条数据时,从内存页中读取该数据,并执行change buffer与这个页相关的操作,从而保证数据逻辑的正确性。
change buffer是可以持久化的,它在内存和磁盘上都有。

更新契机

除了访问会merge change buffer,后台会有定时线程merge change buffer。在数据库关闭时也会进行merge。

大小设置

change buffer占用的是buffer pool的内存,buffer pool是数据库关键的内存,常用的操作有回表等。通过innodb_change_buffer_max_size参数设置,为50时,表示占用50%的buffer pool空间。

使用场景

写多读少。
如果是读多写少,或者读写相当,那么就需要考虑维护change buffer的开销了。

与redo log的关系

从写的角度:

《MySQL听讲(三)——索引》 image.png

从读的角度:

《MySQL听讲(三)——索引》 image.png

读的时候不需要在走redo log。

索引的使用

前缀索引的使用

一般针对于邮箱等,可能需要建立前缀索引。而索引的长度需要分析数据库得知。
通过下面的语句分析出前缀索引的长度:
SELECT 100*count(DISTINCT(left(email,7)))/count(*) from t3;
前缀索引需要一定的损失率来节省空间。一般为5%,即上面的结果大于95。其中数字7位索引的长度。如下建立索引:

ALTER TABLE t3 add index index_email_prev(email(7));

但是,前缀索引有一些不好的地方,1)回表率;2)覆盖索引失效。
回表率可以理解为回表的次数增加。
覆盖索引是指针对于某些特殊的查询条件,如上面的索引如果是针对于email全字段的话,select id,email会更快一些,且只走辅助索引。

反转索引的使用

针对于某些前面分离度不高的字串,如身份证号,前6位为地区码。MySQL原生并不支持反转索引,可以在插入时将字符串反转。在查询时使用reverse函数转换一下查询条件。
根据情况可以执行前缀索引的distinct来判断一下是否需要加前缀索引。

hash索引的使用

针对于上面提及的身份证号,也可以使用hash索引,准确的来说是hash字段索引。因为其索引类型还是BTREE。但hash索引需要额外增加整数字段(数据类型为int unsigned)。
ALTER TABLE test3 add name_crc int UNSIGNED,ADD index index_name(name);
前面的name_crc是字段名。
在存取时可以使用MySQL的hash函数crc32()

在使用该索引时,因hash会有冲突,所以需要这样使用:where crc32(name)=crc('xiaoming') and name='xiaoming'

hash索引只支持等值查询,不支持范围、模糊查询。
hash索引如果是联合索引,则不支持任何单字段查询。

全文索引

只在引擎为MyISAM时支持。
建立语句如下:
ALTER TABLE t3 add FULLTEXT index_full(email,name);

    原文作者:靈08_1024
    原文地址: https://www.jianshu.com/p/86ef8c76432c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞