索引模块除了是数据库最重要的模块之一,也是面试中最经常被问到的,关于索引模块常见问题如下:
为什么要使用索引:
数据库中最小存储单位通常是块或者页,每个块里面都会包含多行数据。而我们在查询一些没有使用索引的数据时,通常都需要进行全表扫描,也就是说需要加载所有的块,然后逐个遍历这些块直到查找出我们需要查找的数据。可想而知这种查询方式在数据量比较大的时候效率是比较慢的,所以我们很多时候都需要避免全表扫描。不过数据库的设计者早已考虑到这一点所以引入了更高效的查询机制,即使用索引。索引的灵感来自于字典,我们都知道字典会记录一些关键信息,例如偏旁部首拼音等,我们通过这些关键信息就可以快速查找到那个字所在的页面。而索引也是如此,数据库能够通过索引记录的关键信息迅速定位目标数据在哪个位置上,就可以避免全表扫描的发生。所以使用索引的目的就是为了让查询更高效。
什么样的信息能成为索引:
主键id,唯一的字段,以及频繁被作为查询条件的字段,若同时多个字段频繁作为查询条件时可以对这几个字段建立组合索引
索引的数据结构:
通常是B+树、Hash以及少数数据库支持的BitMap
接下来简单的说下索引的数据结构,我们都知道索引最常用的数据结构是B+树,在介绍什么是B+树之前,首先得了解二叉查找树和B树,并简单说明一下为什么没有采用二叉树或B树作为索引的数据结构。
现在我们已经知道给字段建立索引的目的是为了帮助我们快速定位到目标数据所在的位置,若让我们自己去设计索引的话,对于快速查找这个需求可能第一时间就会想到二叉查找树之类的树形数据结构。所以本小节先介绍二叉查找树,并一步一步地了解为何在众多的树形结构中会采用B+树作为索引的数据结构。
二叉查找树是一种常用的树形数据结构,二叉查找树的每个节点最多只有左右两个子节点,分别成为左子树和右子树,通常左子树的元素小于它的父节点,而右子树则大于它的父节点。位于最顶端的节点通常称为根节点,二叉查找树的查找算法是二分查找。下图是一颗平衡二叉树,所谓平衡二叉树就是末端左右两个节点的高度相差不超过1:
二叉查找树由于同一级最多只能有两个节点,且对磁盘IO没有优化,因为每次IO读取都只能读两个节点,所以并不能达到较理想的查询速度,不能作为索引的数据结构。
由于二叉树每次只能读取两个节点对磁盘IO没有优化,并且只有左右两个查找路径,树的深度就会随着日益增加的数据量而递增,所以这时候就需要寻找一个每个层级可以有多个节点的多路树形结构,而B树就符合该需求,B树又称为多路平衡查找树,其大致结构如下图:
同一层有m个节点通常称为m阶,一棵m阶B树(balanced tree of order m)是一棵平衡的m路搜索树。它或者是空树,或者是满足下列性质的树:
ceil(m/2)
个子节点Ki (i=1...n)
为关键字,且关键字按顺序升序排序 K(i-1) < Ki
[ceil(m / 2) - 1] <=n <= m - 1
,即任意节点的关键字个数上限比它的子树上限少一个,且对于非叶子节点来说任意节点的关键字个数比它的指向孩子的指针个数少一个①:某节点最左子节点里关键字的值均小于该节点最左关键字的值
②:某节点最右子节点里关键字的值均大于该节点里所有关键字的值
③:某节点除左右以外所有子节点里关键字的值大小,均位于离该子节点指针最近的两个关键字的值之间
B 树虽然已经达到可以用作于索引数据结构的标准,但是还有更好的替代品,那就是B+树,从名字也可以看出B+树相当于是B树的变体。其定义基本与B树相同,除了:
[K[i], K[i + 1])
的子树B+树结构图:
B+树相比于B树及其他树形数据结构来说,更适合用来做存储索引,原因如下:
除了上一小节所介绍的B+ 树索引结构之外,还有一个常用的Hash索引结构。Hash稍微简单一些,就是对索引的key进行一次hash计算,然后就可以定位出数据存储的位置,所以在某些特定场景来说Hash索引要比B+ 树索引更高效。如图:
既然理论上来说Hash索引要比B+ 树索引更高效,但是为什么没有成为主流索引结构呢,这是因为Hash索引存在以下缺点:
BitMap:
除了B+ 树及Hash索引外,还有一种索引结构就是BitMap,即位图索引,但是仅有少量数据库支持,所以这里仅做简略提及。当表中的某个字段只有几种值的时候,例如存储性别信息的字段之类的,在这种字段使用BitMap索引就是最佳的选择。BitMap结构图如下:
但是BitMap有一个很大的缺陷就是锁的粒度会非常的大,在新增和更新数据时,与该数据在同一个位图的数据也会被锁住。
密集索引和稀疏索引的区别:
密集索引和稀疏索引的区别图:
密集索引:叶子节点保存的不仅仅是键值,还保存了位于同一行数据里其他列的信息,由于密集索引决定了表的物理排列顺序,而一个表只能有一个物理排列顺序,所以一个表只能创建一个密集索引
稀疏索引:叶子节点仅保存了键位信息,以及该行数据的地址或主键。所以需要通过数据的地址或主键才能进一步定位到数据。
我们来看看具体到MySQL的主流存储引擎:
InnoDB与MyISAM引擎的检索流程对比:
假设我们对A、B两个字段建立联合索引:(A, B),此时该联合索引的左边是A而右边是B,当执行where A = '' and B = ''
时会走这个(A, B)联合索引,where A = ''
也会走(A, B)联合索引,但是where B = ''
则不会走(A, B)联合索引。这就是所谓的最左匹配原则
在最左匹配原则中,有如下说明:
- 最左前缀匹配原则,非常重要的原则,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的顺序可以任意调整。
- =和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 这样的一种排序规则。所以联合索引中最左字段是绝对有序的,而后一个字段则是无序的了,因此使用除最左字段以外的字段进行条件查询是利用不到索引的,这就是最左匹配原则的成因
答案是否定的,所谓物极必反:
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。