数据库之索引模块

索引模块除了是数据库最重要的模块之一,也是面试中最常常被问到的,关于索引模块常见问题以下:html

  • 为何要使用索引
  • 什么样的信息能成为索引
  • 索引的数据结构
  • 密集索引和稀疏索引的区别

为何要使用索引:mysql

数据库中最小存储单位一般是块或者页,每一个块里面都会包含多行数据。而咱们在查询一些没有使用索引的数据时,一般都须要进行全表扫描,也就是说须要加载全部的块,而后逐个遍历这些块直到查找出咱们须要查找的数据。可想而知这种查询方式在数据量比较大的时候效率是比较慢的,因此咱们不少时候都须要避免全表扫描。不过数据库的设计者早已考虑到这一点因此引入了更高效的查询机制,即便用索引。索引的灵感来自于字典,咱们都知道字典会记录一些关键信息,例如偏旁部首拼音等,咱们经过这些关键信息就能够快速查找到那个字所在的页面。而索引也是如此,数据库可以经过索引记录的关键信息迅速定位目标数据在哪一个位置上,就能够避免全表扫描的发生。因此使用索引的目的就是为了让查询更高效。面试

什么样的信息能成为索引:算法

主键id,惟一的字段,以及频繁被做为查询条件的字段,若同时多个字段频繁做为查询条件时能够对这几个字段创建组合索引sql

索引的数据结构:数据库

一般是B+树、Hash以及少数数据库支持的BitMap数据结构


二叉查找树

接下来简单的说下索引的数据结构,咱们都知道索引最经常使用的数据结构是B+树,在介绍什么是B+树以前,首先得了解二叉查找树和B树,并简单说明一下为何没有采用二叉树或B树做为索引的数据结构。ide

如今咱们已经知道给字段创建索引的目的是为了帮助咱们快速定位到目标数据所在的位置,若让咱们本身去设计索引的话,对于快速查找这个需求可能第一时间就会想到二叉查找树之类的树形数据结构。因此本小节先介绍二叉查找树,并一步一步地了解为什么在众多的树形结构中会采用B+树做为索引的数据结构。性能

二叉查找树是一种经常使用的树形数据结构,二叉查找树的每一个节点最多只有左右两个子节点,分别成为左子树和右子树,一般左子树的元素小于它的父节点,而右子树则大于它的父节点。位于最顶端的节点一般称为根节点,二叉查找树的查找算法是二分查找。下图是一颗平衡二叉树,所谓平衡二叉树就是末端左右两个节点的高度相差不超过1:
数据库之索引模块优化

二叉查找树因为同一级最多只能有两个节点,且对磁盘IO没有优化,由于每次IO读取都只能读两个节点,因此并不能达到较理想的查询速度,不能做为索引的数据结构。


B树

因为二叉树每次只能读取两个节点对磁盘IO没有优化,而且只有左右两个查找路径,树的深度就会随着日益增长的数据量而递增,因此这时候就须要寻找一个每一个层级能够有多个节点的多路树形结构,而B树就符合该需求,B树又称为多路平衡查找树,其大体结构以下图:
数据库之索引模块

同一层有m个节点一般称为m阶,一棵m阶B树(balanced tree of order m)是一棵平衡的m路搜索树。它或者是空树,或者是知足下列性质的树:

  1. 根节点至少有两个子节点
  2. 树中每一个节点最多含有m个子节点(m >= 2)
  3. 除根节点和叶子节点外,其余每一个节点至少有ceil(m/2)个子节点
  4. 全部的叶子节点都位于同一层
  5. 假设每一个非终端节点中包含有n个关键字信息,其中:
    • Ki (i=1...n)为关键字,且关键字按顺序升序排序 K(i-1) < Ki
    • 关键字的个数 n 必须知足:[ceil(m / 2) - 1] <=n <= m - 1,即任意节点的关键字个数上限比它的子树上限少一个,且对于非叶子节点来讲任意节点的关键字个数比它的指向孩子的指针个数少一个
    • 非叶子节点的指针:P[1], P[2], ..., p[M]; 其中 P[1] 指向关键字小于 K[1] 的子树①,P[M] 指向关键大于 K[M - 1] 的子树②,其余 P[i] 指向关键字属于 (K[i - 1], K[i]) 的子树③

①:某节点最左子节点里关键字的值均小于该节点最左关键字的值
②:某节点最右子节点里关键字的值均大于该节点里全部关键字的值
③:某节点除左右之外全部子节点里关键字的值大小,均位于离该子节点指针最近的两个关键字的值之间


B+树

B 树虽然已经达到能够用做于索引数据结构的标准,可是还有更好的替代品,那就是B+树,从名字也能够看出B+树至关因而B树的变体。其定义基本与B树相同,除了:

  1. 非叶子节点的子树指针与关键字个数相同
  2. 非叶子节点的子树指针 P[i],指向关键字值[K[i], K[i + 1])的子树
  3. 非叶子节点仅用来作索引,数据都保存在叶子节点中
  4. 全部叶子节点均有一个链指针指向下一个叶子节点,叶子节点造成的链会按大小排序

B+树结构图:
数据库之索引模块

B+树相比于B树及其余树形数据结构来讲,更适合用来作存储索引,缘由以下:

  • B+ 树的磁盘读写代价更低,B+ 树因为非叶子节点只会存储索引,所以B+ 树的非叶子节点相对于B 树来讲更小,若是把全部同一内部节点的关键字存储在同一盘块中,那么该盘块所能容纳的关键字数量也越多,一次性读入内存中的关键字也就越多,相对来讲IO读写次数也就下降了
  • B+ 树的查询效率更加稳定,由于具体数据存储在叶子节点中,因此不管查询任何数据都须要从根节点走到叶子节点,那么全部查询的长度也就相同,这样每一个数据查询的效率就几乎是相同的
  • B+ 树更有利于对数据库的扫描,B 树在提升了磁盘IO的同时并无解决遍历元素效率低下的问题,而B+ 树只须要遍历叶子节点就能够解决对所有关键字信息的扫描,因此对数据库中频繁使用的范围查询来讲B+ 树更高效

Hash以及BitMap

除了上一小节所介绍的B+ 树索引结构以外,还有一个经常使用的Hash索引结构。Hash稍微简单一些,就是对索引的key进行一次hash计算,而后就能够定位出数据存储的位置,因此在某些特定场景来讲Hash索引要比B+ 树索引更高效。如图:
数据库之索引模块

既然理论上来讲Hash索引要比B+ 树索引更高效,可是为何没有成为主流索引结构呢,这是由于Hash索引存在如下缺点:

  • 由于hash的特性,因此仅仅能知足 “=”,“IN”,不能使用范围查询
  • 没法被用来避免数据的排序操做
  • 不能利用部分索引键查询,由于在使用组合索引的时候,Hash索引是将组合索引里的字段合并后再计算的hash值,而不是单独计算的hash值。因此不使用组合索引里所有字段去查询的话,Hash索引就没法被利用
  • 不能避免表扫描,由于数据量大的时候就会有出现重复Hash较多的状况,那么就得拿出全部相同Hash值的数据来比较才能取到具体的数据,因此广泛来讲数据量越大Hash索引的效率就越低
  • 遇到大量Hash值相等的状况后性能并不必定就会比B+树索引高

BitMap:

除了B+ 树及Hash索引外,还有一种索引结构就是BitMap,即位图索引,可是仅有少许数据库支持,因此这里仅作简略说起。当表中的某个字段只有几种值的时候,例如存储性别信息的字段之类的,在这种字段使用BitMap索引就是最佳的选择。BitMap结构图以下:
数据库之索引模块

可是BitMap有一个很大的缺陷就是锁的粒度会很是的大,在新增和更新数据时,与该数据在同一个位图的数据也会被锁住。


密集索引和稀疏索引的区别

密集索引和稀疏索引的区别:

  • 密集索引文件中的每一个搜索码值都对应一个索引值
  • 稀疏索引文件只为索引码的某些值创建索引项
  • 密集索引和稀疏索引的主要区别就是前者叶子节点保存完整的数据,然后者保存的是指向data的指针

密集索引和稀疏索引的区别图:
数据库之索引模块

密集索引:叶子节点保存的不只仅是键值,还保存了位于同一行数据里其余列的信息,因为密集索引决定了表的物理排列顺序,而一个表只能有一个物理排列顺序,因此一个表只能建立一个密集索引

稀疏索引:叶子节点仅保存了键位信息,以及该行数据的地址或主键。因此须要经过数据的地址或主键才能进一步定位到数据。

咱们来看看具体到MySQL的主流存储引擎:

  • MyISAM:不论是主键索引、惟一索引仍是普通索引都属于稀疏索引,因此MyISAM只有稀疏索引,没有密集索引。而且MyISAM中索引与数据是分开存储的
  • InnoDB:表只会有且只有一个密集索引,其余索引都是稀疏索引。而且InnoDB中索引与数据是存储在同一个文件中的
    • 若一个主键被定义,该主键则做为密集索引
    • 若没有主键被定义,该表的第一个惟一非空索引则做为密集索引
    • 若不知足以上条件,InnoDB内部会生成一个隐藏主键做为密集索引,这个隐藏的主键是一个6字节的自增列
    • 非主键索引存储相关键位和其余对应的主键值,包含两次查找

InnoDB与MyISAM引擎的检索流程对比:
数据库之索引模块


索引额外问题之联合索引最左匹配原则的成因

假设咱们对A、B两个字段创建联合索引:(A, B),此时该联合索引的左边是A而右边是B,当执行where A = '' and B = '' 时会走这个(A, B)联合索引,where A = ''也会走(A, B)联合索引,可是where B = ''则不会走(A, B)联合索引。这就是所谓的最左匹配原则

在最左匹配原则中,有以下说明:

  1. 最左前缀匹配原则,很是重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就中止匹配,好比a = 1 and b = 2 and c > 3 and d = 4 若是创建(a,b,c,d)顺序的索引,d是用不到索引的,若是创建(a,b,d,c)的索引则均可以用到,a,b,d的顺序能够任意调整。
  2. =和in能够乱序,好比a = 1 and b = 2 and c = 3 创建(a,b,c)索引能够任意顺序,mysql的查询优化器会帮你优化成索引能够识别的形式

咱们来作个实验,验证下最左匹配原则。建表sql以下,该表中有一个联合索引:

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  `age` int(11) NOT NULL,
  `sex` varchar(20) NOT NULL,
  `address` varchar(100) NOT NULL,
  `cid` int(11) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_name_age` (`name`,`age`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;

当where条件存在name字段时,会使用索引查询:
数据库之索引模块
数据库之索引模块

当where条件不存在name字段时,则不会使用索引查询:
数据库之索引模块

当where条件存在name字段时,即使是乱序也会使用索引查询,由于MySQL的执行优化器会自动调整顺序以知足使用索引的条件:
数据库之索引模块

参考文章:

如今咱们来回答一下最左匹配原则的成因:

MySQL建立联合索引时,是先对联合索引中最左字段的数据进行排序,在最左字段排序的基础上,再对后一个字段的数据进行排序,相似于order by 字段1,order by 字段2 这样的一种排序规则。因此联合索引中最左字段是绝对有序的,然后一个字段则是无序的了,所以使用除最左字段之外的字段进行条件查询是利用不到索引的,这就是最左匹配原则的成因

数据库之索引模块


索引额外问题之索引是创建越多越好吗

答案是否认的,所谓物极必反:

  • 数据量小的表不须要创建索引,创建索引会增长额外的索引维护开销
  • 数据变动须要维护索引,所以更多的索引意味着更多的维护成本
  • 更多的索引也意味着须要更多的存储空间
相关文章
相关标签/搜索