《MySql技术内幕 InnoDb存储引擎》学习笔记【六 索引和算法】

目录

六 索引和算法

(一)概述

(二)B+树索引

1 聚集索引

2 非聚集索引

3 B+树索引的分裂

4 B+树索引的管理

(三)B+树索引的使用

1 联合索引

2 覆盖索引

3 优化器选择不使用索引的情况

4 索引提示

5 Multi-Range Read优化

6 Index Condition Pushdown优化

(四)哈希索引

(五)全文索引

1 概述

2 倒排索引

3 InnoDB全文检索

4 全文检索的使用


六 索引和算法

(一)概述

索引时一种帮助数据库高效查询数据的数据结构,InnoDB支持B+树索引、Hash索引和Full Text索引:

  1. B+索引是目前关系型数据库中最常用的索引,B不代表Binary,而是代表Balance。
  2. Hash索引在InnoDB中的体现是自适应哈希索引,不能人为创建Hash索引。
  3. Full Text索引类似文本的匹配,但不是简单的通过like查询。

(二)B+树索引

B+树是为磁盘或其他直接存取设备设计的一种平衡查找树。我们知道在InnoDB中,数据文件是按B+树结构组织的,也就是数据文件本身就是索引文件,数据文件存放于磁盘上,那么B+树作为InnoDB的索引结构是非常合适的,为什么使用B-Tree(B+Tree)具体参考:http://blog.codinglabs.org/articles/theory-of-mysql-index.html?tdsourcetag=s_pctim_aiomsg

1 聚集索引

在InnoDB中,主键列默认是聚集索引,我们假设有如下的表结构:

table example(id, name, age),id为主键,name为人为建立的辅助索引

如下是建立在主键id列的聚集索引:

《MySql技术内幕 InnoDb存储引擎》学习笔记【六 索引和算法】_第1张图片

可以看到,在聚集索引中,叶子节点又称为数据页,按照主键顺序排列,其中保存了完整的数据,由于InnoDB是索引组织表,因此根据对主键的培训查找和范围是非常快的。

注意:由于实际的一个数据页只能按照一棵B+树组织,因此每张表只能有一个聚集索引。

2 非聚集索引

非聚集索引的叶子节点并不包含行记录的完整数据,而是包含相应记录的聚集索引键。非聚集索引不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引。如下是建立在name列的非聚集索引:

《MySql技术内幕 InnoDb存储引擎》学习笔记【六 索引和算法】_第2张图片

3 B+树索引的分裂

B+树的索引页的分裂并不总是从页的中间记录开始,InnoDB的Page Header中有以下几个部分来保存插入的顺序信息:

  1. PAGE_LAST_INSERT
  2. PAGE_DIRECTION
  3. PAGE_N_DIRECTION

通过这些信息,InnoDB可以决定是向左还是向右进行分裂,如何选取分裂点等。

4 B+树索引的管理

1)索引的管理

索引的创建和删除可以通过两种方法:

通过ALTER TABLE语句:

《MySql技术内幕 InnoDb存储引擎》学习笔记【六 索引和算法】_第3张图片

通过Create/Drop INDEX语句:

可以设置对整个列的数据进行索引,也可以指索引一个列的一部分数据,如表t,列b为varchar(8000),先创建一个只索引前100个字符的索引:

创建联合索引:

2Fast Index Creation

MySql5.5版本前,索引的添加或删除操作过程为:

  1. 首先创建一张新的临时表
  2. 然后把数据导入临时表
  3. 接着删除原表
  4. 最后把临时表改名为原表名

当对一张大表进行索引添加和删除操作时,需要很长的时间,从InnoDB1.0.x版本开始支持一种称为Fast Index Creation的索引创建方式。

对于非聚集索引,InnoDB先对表加一个S锁,然后创建索引,不需要新建表,FIC的方式只限定于非聚集索引。

(三)B+树索引的使用

1 联合索引

联合索引是指对表上的多个列进行索引,MySQL联合索引是最左优先原则的。最左优先就是说组合索引的第一个字段必须出现在查询组句中,这个索引才会被用到。只要组合索引最左边第一个字段出现在Where中,那么不管后面的字段出现与否或者出现顺序如何,MySQL引擎都会自动调用索引来优化查询效率。根据最左匹配原则可以知道B-Tree建立索引的过程,比如假设有一个3列索引(a, b, c),那么MySQL只会会建立三个索引(a), (a, b), (a, b, c)。

当使用a=xxx或a=xxx AND b=yyy或a=xxx AND b=yyy AND c=zzz或a=xxx AND c=yyy时都会使用索引,但a=xxx AND c=yyy这种情况并不能直接通过联合索引得到结果,因为(a, c)未排序,需要再执行一次filesort排序。

2 覆盖索引

InnoDB支持覆盖索引,即从非聚集索引中就可以查询到的数据,则不会再去查询聚集索引中查询。

另一方面,在进行统计查询时InnoDB通常会选择通过非聚集索引查询,这样可以减少IO操作,例如SELECT COUNT(*) FROM a;

此外,对于联合索引,例如(a, b),一般是不可以直接通过b作为查询条件的,但在统计查询中,b可以作为查询条件,这是优化器会选择这个联合索引进行统计查询。

3 优化器选择不使用索引的情况

当进行范围查询时,如下:

SELECT * FROM t WHERE a >1000 AND a < 10000 (a为非聚集索引列)

查询操作通常需要分为两步,首先在列a的索引中查找书签,然后根据书签到聚集索引中查找完整数据。但优化器会对这个查询进行优化,如果查询的数据量较少,则按前面所说的步骤查询,若果查询的数据占整张表的一大部分时(一般是20%左右),优化器会选择通过聚集索引来查找数据。

4 索引提示

MySql支持索引提示(INDEX HINT),显式告诉优化器使用哪个索引,通常在以下两种情况时可能需要使用索引提示:

  1. 优化器错误的选择了某个索引,导致SQL语句运行的很慢。
  2. 某个SQL语句可以选择的索引非常多,导致优化器的选择的时间开销可能会大于SQL本身。

SELECT * FROM t USE INDEX(a) WHERE xxxx

USE INDEX用于向优化器给出建议,但最后的选择还是由优化器决定的。

SELECT * FROM t FORCE INDEX(a) WHERE xxxx

FORCE INDEX用于强制优化器使用的索引。

5 Multi-Range Read优化

MySql5.6版本开始支持Multi-Range Read(MRR)优化,目的是为了减少磁盘的随机访问,并将随机访问优化为较为顺序的数据访问。

对于范围查询和JOIN查询操作,MRR的工作方式如下:

  1. 将查询得到的非聚集索引键值放入一个缓存中,这时是按非聚集索引键值排序的。
  2. 将缓存中的键值根据聚集索引键值(主键值)排序。
  3. 根据聚集索引键值的顺序来访问数据。

MRR优化有以下好处:

  1. 是数据访问变得较为顺序
  2. 减少缓冲池中页被替换的次数,这是因为随机的离散访问会导致数据页的频繁替换,而根据主键顺序访问可以降低页的替换频率。
  3. 批量处理对键值的查询操作

可以通过read_rnd_buffer_size参数配置键值缓冲区的大小。

6 Index Condition Pushdown优化

Index Condition Pushdown是MySql5.6开始支持的一种根据索引进行查询的优化方式。

考虑索引查询的过程,首先根据索引来查找记录,然后再通过WHERE条件比对查找出的记录是否符合条件。Index Condition Pushdown会在数据库根据索引查找记录的同时通过WHERE条件进行过滤,提高数据库的整体查询性能。

(四)哈希索引

InnoDB使用哈希算法来实现字典查找,通过链地址法解决冲突,哈希函数采用除法散列的方式。

我们知道除法散列的哈希函数为:h(k) = k mod m

那么InnoDB中是如何确定k和m的呢?

首先,对于innodb_buffer_pool_size=10M,共有640个16KB的页,哈希表需要分配640 * 2= 1280个槽,选取大于等于1280的第一个质数1399作为m。

然后,对于表空间space_id和偏移量offset,k = space_id << 20 + space_id + offset。

InnoDB中的哈希索引是自适应的,不能认为创建。

(五)全文索引

1 概述

我们知道B+索引是可以通过前缀进行查找的,例如:

SELECT * FROM t WHERE a like ‘xxx%’;

但通常情况下我们需要检索的是内容包含某个特定次的记录,例如:

SELECT * FROM t WHERE a like ‘%xxx%’;

这种查询通常称为全文检索,从InnoDB1.2.x版本开始,InnoDB支持了全文检索。

2 倒排索引

全文检索通常使用倒排索引来实现。倒排索引是一种索引数据结构,通过倒排表存储单词与单词位置的映射关系,通常是一个二元组,表现形式有以下两种:

  1. inverted file index:{key, 记录Id}
  2. full inverted index:{key, (记录Id, 位置)}

例如,建立一个full inverted index,我们的数据如下:

Message1:We are good

Message2:How are you

建立倒排表如下:

No

Key

Location

1

We

(Message1, 1)

2

are

(Message1, 2), (Message2, 2)

3

good

(Message1, 3)

4

How

(Message2, 1)

5

you

(Message2, 3)

3 InnoDB全文检索

InnoDB从1.2.x版本开始支持全文检索,其采用full inverted index方式建立倒排表(辅助表),key为word字段,location为ilist的二元组字段,在InnoDB中,为了提高全文检索的并行性能,共有6张倒排表,每张表根据word的Latin编码进行分区。

倒排表是持久的表,存放于磁盘上,在InnoDB的全文索引中,还有一个Full Text Search Index Cache(全文检索索引缓存),用于提高全文检索性能。

Full Text Search Index Cache是一棵红黑树,根据(word, ilist)进行排序。当插入新数据的事务提交,首先更新FTS Index Cache,然后在进行全文检索时,将Cache中的字段合并到磁盘上的倒排表中。

可以通过innodb_ft_cache_size控制FTS Index Cache的大小,默认为32M,当Cache满时会自动将Cache中的字段刷新到倒排表中。

当删除数据时,InnoDB不会删除倒排表中的记录,而是删除FTS Index Cache中的记录,并将被删除的数据记录在对应的DELETE表中,这样一来,倒排表和DELETE表会越来越大,为此,InnoDB提供了OPTIMIZE TABLE命令,用于手工删除,这个命令还会进行一些其他操作,可以通过innodb_optimize_fulltext_only参数配置只对倒排索引进行操作,可以通过innodb_ft_num_word_optimize来限制每次实际删除的word数量,默认为2000。

4 全文检索的使用

MySql数据库通过MATCH()或AGAINST()语法支持全文检索查询,MATCH指定需要被查询的列,AGAINST指定使用何种方法进行查询,下面介绍各种查询模式:

1Natural Language

全文检索通过MATCH函数进行查询,默认采用Natural Language模式,表示查询带有指定word的记录,如下:

SELECT * FROM t

WHERE MATCH(content)

AGAINST (‘love’ IN NATURAL LANGUAGE MODE);

由于MATCH默认是Natural Language模式,上述查询还可写为:

SELECT * FROM t

WHERE MATCH(content)

AGAINST (‘love’);

在WHERE条件中使用MATCH函数,查询结果是根据相关性降序排序的,相关性的计算依据以下四个条件:

  1. word是否在文档中出现
  2. word在文档中出现的次数
  3. word在索引列中的数量
  4. 多少个文档包含该word

参数innodb_ft_min_token_size(默认为3)和innodb_ft_max_token_size(默认为84)控制查询字符的长度,当不在这个范围内时,忽略该词的搜索,

2Boolean

MySql数据库允许使用IN BOOLEAN MODE修饰符来进行全文检索,当使用该修饰符时,一些字符会有特殊的含义,具体如下:

  1. + 表示该word必须存在
  2. - 表示该word必须排除

如:MATCH(body) AGAINST(‘ +Pease -hot‘ IN BOOLEAN MODE)表示必须包含Pease字符串,必须排除hot字符串。

MATCH(body) AGAINST(‘ Pease hot‘ IN BOOLEAN MODE)表示包含Pease或hot。

  1.  (no operator)表示该word是可选的,但是如果出现,其相关性会更高
  2. @distance表示查询的多个单词之间的距离是否在distance之内,distance的单位是字节。这种全文检索也称为Proximity Search.

如:MATCH(body) AGAINST(‘ “Pease pot” @30‘ IN BOOLEAN MODE),表示字符串Pease和pot的间距必须在30字节以内。

  1. > 表示出现该单词时增加相关性
  2. < 表示出现该单词时降低相关性

MATCH(body) AGAINST(‘ >good

  1. ~ 表示允许出现该单词,但出现时相关性为负
  2. * 表示以该单词开头的单词,如lik*
  3. “ 表示短语

如:MATCH(body) AGAINST(‘ “Pease hot”‘ IN BOOLEAN MODE)表示包含Pease hot。

3Query Expansion

MySql数据库还支持全文检索的扩展查询,这种查询通常在查询的关键词太短,用户需要隐含知识时进行。比如,用户对单词database的查询,用户可能虚妄查询的不仅仅是包含database的文档,可能还包括那些包含MySql、Oracle、DB2等关键词的文档,我们可以通过在查询短语后添加WITH QUERY EXPANSION或IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION来启用,如:

SELECT * FROM t

WHERE MATCH(title, body)

AGAINST(‘database’ WITH QUERY EXPANSION )

开开启了扩展查询后,查询分为两个阶段:

  1. 第一阶段:根据搜索的关键词进行全文检索。
  2. 第二阶段:将第一阶段的查询结果中包含的关键词以及和关键词相连的词作为关键词再进行查询,比如第一阶段根据database查询出记录中包含database MySql、DB2 database等记录,那么第二阶段的查询关键词会包含database、MySql、DB2。

特此声明:本系列博客为均为《MySql技术内幕 InnoDb存储引擎》读书笔记,存在错误还请指正

参考资料

《MySql技术内幕 InnoDb存储引擎》

你可能感兴趣的:(MySql,InnoDB,索引和算法)