第五章 索引与算法(学习笔记)

  1. MySQL索引为何使用B+树数据结构,而不是B树,红黑树等?

  1.1 二叉查找树/二叉搜索树数据库

  下图b为二叉查找树的典型结构,其特色为:  数组

  • 任意节点左子树不为空,则左子树的值均小于根节点的值
  • 任意节点右子树不为空,则右子树的值均大于于根节点的值
  • 任意节点的左右子树也分别是二叉查找树
  • 没有键值相等的节点

   

  局限性: 若是数据排序好插入二叉查找树时,会退化为链表,此时时间复杂度为O(n),最好的状况是O(log n)。缓存

  1.2 AVL 树(Adelson-Velsky and Landis Tree,平衡二叉树)数据结构

  因为二叉查找树的局限性,引入了AVL树,在插入数据时,调整这棵树,让它的节点尽量均匀分布。该树节点左右子树深度之差只能是0,1,-1. 若是插入数据后,不知足平衡性,会经过旋转来实现平衡,具体的经过旋转保证平衡的四种操做参见专栏https://www.bilibili.com/read/cv8748171。性能

  局限性:旋转是很是耗时的,故而AVL树适合用于插入删除次数比较少,但查找多的状况。 优化

  1.3 红黑树.net

  红黑树是平衡二叉树的一种变体。红黑树确保没有一条路径会比其它路径长出两倍。它是一种弱平衡二叉树(因为是弱平衡,能够推出,相同的节点状况下,AVL树的高度低于红黑树),相对于要求严格的AVL树来讲,它的旋转次数少,因此对于搜索、插入、删除操做较多的状况下,就用红黑树。Java8中,hashmap底层数据结构是基于数组和链表(红黑树 )来实现的,它之因此有至关快的查询速度主要是由于它是经过计算散列码来决定存储的位置。经过key值计算hash码值,将其传递进数组,若hash码值冲突则采用链表链接,若该hashmap容量大于64且该链表长度大于8则将该链表转化为红黑树,若链表长度减小到6时候,则将红黑树转化回链表。设计

  

  红黑树的特征:3d

  • 每一个节点非红即黑指针

  • 根节点是黑的

  • 每一个叶节点(叶节点即树尾端NULL指针或NULL节点)都是黑的

  • 若是一个节点是红的,那么它的两儿子都是黑的

  • 对于任意节点而言,其到叶子点树NULL指针的每条路径都包含相同数目的黑节点

  • 高度始终保持在h = logn红黑树的查找、插入、删除的时间复杂度最坏为O(log n)

  一样的经过旋转保证平衡的操做参见专栏  https://www.bilibili.com/read/cv8748171。

  为何文件系统和数据库常使用B树及其变体B+树存储数据索引呢?

  红黑树每一个节点最多只有两个子节点,而B树,B+树是多路查找树,分支多则层数变少。文件系统和数据库的索引都是存放在磁盘上。磁盘IO次数与树的深度相同,因为磁盘特殊的结构,磁盘的存取速度每每是主存的几百分之一,所以为了提升效率,要尽可能减小磁盘I/O,即下降树的深度。故而不使用红黑树。

  那能否设计为无限多路树,即退化为有序数组?

  文件系统和数据库的索引都是存在硬盘上的,而且若是数据量大的话,不必定能一次性加载到内存中。B+树的多路存储容许每次加载B+树的一个结点,然后一步步往下找。

  1.4 B树和B+树

  B树和B+树的典型结构以下图所示,关于其特性能够参考博客 https://blog.csdn.net/herr_kun/article/details/80550652:

      

                           B树

 

                                                                                                          B+ 树

  这里讨论的是,为何使用B+树数据结构存储索引(帮助MySQL高效获取数据的数据结构)?

  • 磁盘读写代价更低    B+树的内部结点并无指向关键字(root中的5,28,65)具体信息的指针。所以其内部结点相对于B树更小。若是把全部同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中须要查找的关键字也就越多。相对来讲IO读写次数也就下降了。
  • 遍历数据更加方便     多数时候须要从数据库中select多条数据,比如按照id进行排序后选100条。如果是多条的话,B树需要做局部的中序遍历,可能要跨层访问。而B+树由于所有数据都在叶子结点不用跨层,同时由于有链表结构,只需要找到首尾,通过链表横向遍历便可。为了提升效率,要尽可能减小磁盘I/O。为了达到这个目的,磁盘每每不是严格按需读取,而是每次都会预读,即便只须要一个字节,磁盘也会从这个位置开始,顺序向后读取必定长度的数据放入内存(不须要寻道时间,只需不多的旋转时间)。这样作的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也一般会立刻被使用。
  • 查询效率更加稳定     非终结点并非最终指向文件内容的结点,而只是叶子结点中关键字的索引。因此任何关键字的查找必须走一条从根结点到叶子结点的路。全部关键字查询的路径长度相同,致使每个数据的查询效率至关 

  2. B+树索引

  B+树索引在数据库中有一个特色是高扇出(指该模块直接调用的下级模块的个数)性,B+树的高度通常都在2~4层,即查询某一键值的行记录时最多只须要2到4次IO,查询时间在0.02~0.04秒。B+树索引能够分为汇集索引和辅助索引。

  2.1 汇集索引

  InnoDB存储引擎是索引组织表,即表中数据按照主键顺序存放。汇集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为整张表的行记录数据,也将汇集索引的叶子节点称为数据页。

  数据页上存放的是完整的每行的记录,而在非数据页的索引页中,存放的仅仅是键值及指向数据页的偏移量。汇集索引的存储并非物理上连续的,而是逻辑上连续的。

  

 

  汇集索引对于主键的排序查找和范围查找速度很是快。若是查找主键某一范围内的数据,经过叶子节点的上层中间节点就能够找到页的范围,以后直接读取数据页便可。

   2.2 辅助索引

  对于辅助索引(secondary index),叶子节点并不包含行记录的所有数据。叶子节点除了包含键值之外,每一个叶子节点中的索引行中还包含了一个书签(bookmark,相应行数据的汇集索引键)。该书签用来告诉InnoDB存储引擎哪里能够找到与索引相对应的行数据。  当经过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并经过叶级别的指针得到指向主键索引的主键,而后再经过主键索引来找到一个完整的行记录。

  

 

   2.3 索引的建立与删除

  

    

 

  2.4 Fast Index Creation

  MySQL数据库对主键的添加和删除过程为:

  • 首先建立一张新的临时表,表结构经过命令ALTER TABLE新定义的结构
  • 而后把原表中数据导入到临时表
  • 接着删除原表
  • 最后把临时表重命名为原来的表名

  对于辅助索引的建立,InnoDB存储引擎会对建立索引的表加上一个S锁。在建立过程当中,不须要重建表,故而速度更快。删除辅助索引时,InnoDB存储引擎只须要更新内部视图,并将辅助索引的空间标记为可用,同时删除MySQL数据库内部视图上对该表的索引定义便可。

  3. Cardinality 值

  若是某个字段的取值范围很广,几乎没有重复,既属于高选择性,最适合用于B+树索引。Cardinality值表示索引中不重复记录数量的预估值。Cardinality/n_rows_in_table应尽量接近1.

  4. B+树索引的使用

  4.1 联合索引

  联合索引对表上的多个列进行索引。联合索引也是一颗B+树。

  对于查询SELECT * FROM TABLE WHERE a=xxx and b=xxx/ SELECT * FROM TABLE WHERE a=xxx ORDER BY b 可使用(a,b)联合索引。

  对于查询SELECT * FROM TABLE WHERE a=xxx,也可使用联合索引。

  可是对于SELECT * FROM TABLE WHERE b=xxx,不能使用联合索引,由于b列不是排好序的。

   

 

  4.2 覆盖索引

  InnoDB存储引擎支持覆盖索引,即从辅助索引中就能够获得查询的记录,而不须要查询汇集索引中的记录。使用覆盖索引的一个好处是辅助索引不包含整行记录的全部信息,故其大小要远小于汇集索引,所以能够减小大量的IO操做。

  对于InnoDB存储引擎的辅助索引,因为其包含了主键信息,所以其叶子节点存放的数据为(primary key1, primary key2, ..., key 1, key2, ...)例如,下面语句可以使用一次覆盖索引完成:

  SELECT key2 FROM table WHERE key1 = xxx

  SELECT primary key2, key2 FROM table WHERE key1=xxx

  SELECT primary key1, key2 FROM table WHERE key1=xxx

  SELECT primary key1, primary key2, key2 FROM table WHERE key1=xxx

  4.3 优化器选择不使用索引的状况

  在某些状况下,当执行EXPLAIN命令进行SQL语句分析时,会发现优化器并无选择索引去查找数据,而是经过扫描汇集索引,也便是直接进行全表扫描来获得数据。这种状况多发生于范围查找、JOIN连接操做等状况。例如:

  SELECT * FROM orderdetails WHERE orderid>10000 and orderid<102000;

  上述扫描能够经过orderid辅助索引来进行扫描,可是实际优化器使用汇集索引进行扫描。

  这时由于用户选取的数据是整行信息,而OderID索引查询到指定数据,还须要进行一次书签访问来查找整行数据信息。虽然OderID索引中数据是顺序存放的,可是再进行一次书签查找的数据则是无序的,即磁盘上的离散读操做。若是要求访问的数据量很小,则优化器仍是会选择辅助索引,可是当访问的数据占整个表中数据超过20%左右,优化器会选择汇集索引来查找数据。可是若使用的磁盘是固态硬盘,随机读操做很快,能够经过使用FORCE INDEX来强制使用某个索引:

  SELECT * FROM orderdetails FORCE INDEX(OrderID)WHERE orderid>10000 and orderid<102000;

  

 

   

 

   4.4 索引提示

  MySQL数据库支持索引提示,显式地告诉优化器使用哪一个索引,有两种状况可能用到INDEX HINT:

  • MySQL数据库地优化器错误的选择了某个索引,致使查询很慢。这种状况较为少见,大多数时候优化器工做的很是有效
  • 某SQL语句能够选择的索引很是多,这时优化器进行各个执行路径地成本分析开销可能会大于SQL语句自己。DBA或开发人员分析最优的索引选择,而后经过FORCE INDEX来强制优化器按指定索引完成查询

  4.5 Multi-Range Read优化

  MRR地好处:

  • MRR使数据访问变得较为顺序。在查询辅助索引时,首先根据获得的查询结果,按照主键进行排序,并按照主键排序地顺序进行书签查找
  • 减小缓冲池中页被替换地次数
  • 批量处理对键值的查询操做

  对于InnoDB和MyISAM存储引擎的范围查询和JOIN查询操做,MRR工做方式为:

  • 将查询获得的辅助索引键值存放于一个缓存中,这时缓存中的数据根据辅助索引键值排序的
  • 将缓存中的键值根据RowID进行排序
  • 根据RowID的排序顺序来访问实际地数据文件

   此外,MRR还能够将某些范围查询,拆分为键值对,以此来进行批量的数据查询。例如:SELECT * FROM t WHERE key_part1 >= 1000 AND key_part1<2000 AND key_part2 = 10000.

  若没有MRR,此时查询类型为range,SQL优化器会先将key_part1大于1000且小于2000的数据都取出,而后再按key_part2的条件进行过滤。

  若启用了MRR优化,优化器会先将查询条件进行拆分((1000,1000),(1001,1000)(1002,1000)...(1999,1000)),而后再进行数据查询。

  4.6 Index Condition Pushdown (ICP) 优化

   在进行索引查询时,首先根据索引查找记录,而后判断是否能够进行WHERE条件的过滤,也就是将WHERE的部分过滤操做放在了存储引擎层。在某些查询下,能够大大减小上层SQL层对记录的索取,从而提升数据库的总体性能。

  ICP优化支持range,ref, eq_ref, ref_or_null类型的查询,当优化器选择ICP优化时,可在执行计划的列Extra看到Using Index Condition提示。

  5. hash索引

  hash索引进行字典类型的数据查询(SELECT * FROM TABLE WHERE index_col='xxx')的时间复杂度为O(1)。可是对于范围查找却无能为力。

  6. 全文检索

  6.1 概述

  下面的SQL语句能够查询博客内容以xxx开头的文章,而且只要content添加了B+树索引,就能利用索引进行快速查询。

  SELECT * FROM blog WHERE content like 'xxx%'

  然而,更多的时候,用户须要查询的是博客内容包含单词xxx的文章,即:

  SELECT * FROM blog WHERE content like '%xxx%'

  全文检索(full-text search)是将存储于数据库中的整本书或整篇文章中的任意内容信息查找出来的技术。它能够根据须要得到全文中有关章、节、段、句、词等信息,也能够进行各类统计和分析。

  6.2 倒排索引

  倒排索引也是一种索引结构。它在辅助表中存储了单词与单词自身在一个或多个文档中所在位置之间的映射。有两种表现形式:

  • inverted file index {单词,单词所在文档ID}
  • full inverted index {单词,(单词所在文档的ID,在文档中的位置)}

  

  

  

 

   6.3 InnoDB 全文检索

  在InnoDB存储引擎中,将(documentid, position)视为一个ilist。所以,在全文检索的表中,有两个列,一个是Word段,一个是ilist段,这个表称为Auxiliary Table(辅助表),存放于磁盘上。FTS Index Cache(全文检索索引缓存)是一个红黑树结构,用来提升全文检索的性能。参数innodb_ft_cache_size用来控制FTS Index Cache的大小,默认值是32M。当该缓存满时,会将其中的(word,ilist)分词信息同步到磁盘的Auxiliary Table中。增大该参数能够提升全文检索的性能,可是在宕机时,未同步到磁盘中的索引信息须要更多时间来回复。

  stopword列表表示该列表中的Word不须要对其进行索引分词操做,例如单词the。

  InnoDB 全文检索存在如下限制:

  • 每张表只能有一个全文检索的索引
  • 由多列组合而成的全文检索的索引列必须使用相同的字符集与排序规则
  • 不支持没有单词界定符的语言,如中文,日语,韩语等

  6.4 全文检索

  语法为:

  

 

  正负表示这个单词必定出现或者必定不存在。

    

相关文章
相关标签/搜索