索引是存储引擎用于快速找到记录的一种数据结构,良好的索引对性能的影响十分重要。一般而言,索引优化是对查询性能优化最有效的手段。
1.索引基础
尽管良好的索引能够帮助我们优化查询效率,但它同时也需要占用一定的存储空间,由于计算机内存有限,索引无法全部装入内存,必然需要依赖外部存储介质,比如磁盘。而内存的读写速度是磁盘的成千上万倍(与具体实现有关),因此,核心问题是“如何减少磁盘读写次数”。
下面,我就会一步步分析,MySQL到底是如何选择合适的数据结构来保存索引的。在MySQL中,主要有四种类型的索引,分别为:B-Tree索引,Hash索引,Fulltext索引(MyISAM 表)和R-Tree索引,本文讲的是B-Tree索引。
1.1 局部性原理
相信学过计算机操作系统课程的朋友,都对局部性原理有所了解,简单地说,它可以分为以下两种:
-
时间局部性(Temporal Locality):如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。
-
空间局部性(Spatial Locality):在最近的将来将用到的信息很可能与现在正在使用的信息在空间地址上是临近的。
为了提高效率,磁盘往往不是严格按需读取,而是根据局部性原理,每次都会预读。由于磁盘顺序读取的效率很高(不需要寻道时间,只需很少的旋转时间),因此对于具有局部性的程序来说,预读可以提高I/O效率。
1.2 索引数据结构的选择
首先,链表的索引结构不用多言,效率太低,时间复杂度是O(N),而数组+二分查找的复杂度虽然可以提升到O(lgn),但是数组的插入元素以及删除元素的效率很低,因此使用数组做为索引结构并不合适。
另外,在选择数据库索引的结构的时候,要考虑到另一个问题。索引是存在于磁盘中,当索引非常大的时候,达到几个G的时候,无法一次加载到内存中。
考虑到上面两个因素,数据库中索引使用的是树形结构。
平衡二叉树
我们知道,二叉树的查找的时间复杂度是O(log2N),其查找效率与深度有关,而普通的二叉树可能由于内部节点排列问题退化成链表,这样查找效率就会很低。因此平衡二叉树是更好的选择,因为它保持平衡,即通过旋转调整结构保持最小的深度。其查找的时间复杂度也是O(log2N)。
然后,我们说的平衡二叉树结构,指的是逻辑结构上的平衡二叉树,其物理实现是数组。然后由于在逻辑结构上相近的节点在物理结构上可能会差很远。因此,每次读取的磁盘页的数据中有许多是用不上的。因此,查找过程中要进行多次磁盘读取操作。
综上,平衡二叉树并不适合。
B树
平衡二叉树没能充分利用磁盘预读功能,而B树是为了充分利用磁盘预读功能来而创建的一种数据结构。
B数就像一棵“m叉搜索树”(m是子树的最大个数),进一步将时间复杂度减小为O(logm(n)),并且,B树的每个节点可以存储多个关键字,它将节点大小设置为磁盘页的大小,充分利用了磁盘预读的功能。每次读取磁盘页时就会读取一整个节点。也正因每个节点存储着非常多个关键字,树的深度就会非常的小。进而要执行的磁盘读取操作次数就会非常少,更多的是在内存中对读取进来的数据进行查找。
但B树也存在一些问题,比如并没有解决元素遍历的效率低下的问题,正是为了解决这个问题,B+树应运而生。
B+树
B+树就是应数据库所需出现的一种B树的变形树。其与B树的主要差异在于:
-
在B+树中,具有n个关键字的节点只含有n棵子树,即每个关键字对应一颗子树,而B树具有n个关键字的节点只含有(n+1)棵子树
-
所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接。 而B 树的叶子节点并没有包括全部需要查找的信息
-
所有的非叶子结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。.而B 树的非终节点也包含需要查找的有效信息
综上,数据库索引采用B+树的数据结构,利用了磁盘预读原理,将一个节点的大小设为等于一个页(页是计算机管理存储器的逻辑块,硬件及操作系统往往将主存和磁盘存储区分割为连续的大小相等的块,每个存储块称为一页),这样每个节点只需要一次I/O就可以完全载入。同时,每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里。
2.索引其他类型概述
上文中我们提到过,除了使用最多的B+树索引之外,MySQL还提供了Hash索引,Fulltext索引和R-Tree索引,下面也简单介绍下。
Hash索引
Hash索引,顾名思义,基于哈希表实现,对于每一行数据,存储引擎都会对所有的索引列计算一个哈哈希码(hash code),将所有哈希码存储在索引中,同时在哈希表中保存指向对应数据行的指针。
在MySQL中,只有Memory引擎显示支持哈希索引,这也是它的默认索引类型。
由于索引自身只需要存储哈希值,索引结构紧凑,查找的速度非常快,但也有一些限制:
- Hash索引数据不是按照索引值顺序存储的,无法用于排序
- 仅支持等值比较查询,不支持任何范围查询
- 不支持部分索引列匹配查找。比如在数据列(A,B)建立索引,如果查询只有数据列A,则无法应用索引
Fulltext索引
Fulltext索引比较特殊,它查找的是文本中的关键词,而不是直接比较索引中的值,更类似于搜索引擎做的事情,而不是简单的where条件匹配。
R-Tree索引
R-Tree索引,即空间数据索引,可以用做地理数据存储,但在MySQL中支持并不完善,基本不用。