MySQL笔记2:索引之MySQL索引选型

87 阅读4分钟

查询是数据库最重要的的功能之一,业务数据保存在数据库中,查询的效率直接影响了业务的性能。同时修改数据时也需要先查找到数据所在的位置,然后才能进行修改。

索引就是为提升查询效率而设计的数据结构。索引的本质是按照一定的结构将关键字与它对应的记录进行关联,以使后续通过关键字可以快速的查找到所需的数据。索引有多种类型,看一下mysql是如何选择的。

1.  哈希表

哈希表是一个非常常用的索引结构。哈希表中通过哈希算法将关键字转为为一个地址,而这个地址中存放的就是关键字对应的数据记录。

但hash值可能会冲突(hash碰撞),这时一种常用的解决方法是,将冲突的值通过链表连接起来。如下:

1_AgAABe_bA0WfH2Tlh-9KmYtyOydtlRMI.png 查找是只需要计算关键字的hash值,即可得到查找的值的地址,若地址下是一个链表则顺序比较链表中的值。

从算法时间复杂度分析来看,哈希算法时间复杂度为O(1),检索速度非常快。但它却不适合mysql,因为SQL中有一个常常见的查询情形:范围查询,如下:

select * from user where id >3;

使用hash表时,由于范围没有一个确定的hash关键字,所以hash就失效了,只能将所有数据都查询出来逐个进行比较,这时查询的复杂度为O(n),显然是不能接受的。

2.  二叉查找树(BST)

二叉查找树是一个支持数据快速查找的树形数据结构,其具有以下特性:

  1. 若左子树不为空,则左子树上所有节点值均小于其根节点。
  2. 若右子树不为空,则右子树上所有节点值均大于其根节点。
  3. 树的左右子树也是二叉搜索树。

如下:

2_AgAABe_bA0WQKqILtDJKUroi1f4Qt6dC.png

在这个二叉搜索树中,查找值为7的节点时,只需从根节点开始比较‘8’、‘3’、‘6’、‘7’这个四个节点。最坏的情况下查找的节点为叶子节点,需要lgN次查询。

虽然二叉查找树的时间复杂度为lgN,但在极端情况下二叉搜索树会退化为一个链表。当添加到树的数据都是按序插入时,二叉搜索树就会呈现为一个链表的结构,如下:

3_1.png

这种情况下的查找就要逐个遍历每一个节点进行查询,这时情况下时间复杂度就变为了O(n)。

3. 二叉平衡树和红黑树

二叉搜索树存在不平衡的问题,若可以在插入数据时对二叉树进行平衡调整,使得二叉树始终处于一个平衡的状态,而这就是二叉平衡树。

但绝对平衡的二叉树,在插入和删除节点时需要多次调整树中的节点,插入和删除效率将变慢。并且可能每次插入或删除都需读取到整个树,然后对树中多个节点进行调整,最后还需要将整棵树保存,当树中的节点非常庞大时无疑效率会很低。

而红黑树时二叉平衡树的一个变种,它并不追求“完全平衡”,只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。

红黑树能够以O(log2 n) 的时间复杂度进行搜索、插入、删除操作。此外,由于它的设计,任何不平衡都会在三次旋转之内解决。

红黑树看着是一个非常不错的索引结构,但它不适用于数据库。因为数据库的数据都是存放在磁盘当中的,每个节点的数据查询都需要一次磁盘的IO。假设有一个100万行数据的表,即使是使用二叉平衡树做到了绝对平衡,那树的高度也有20,意味着要进行20次磁盘IO,在机械硬盘时代,一次随机的磁盘IO大概在10ms作用,20次就需要200ms,这个耗时显然不能接受。

4. B+树

MySQL最终选择的是二叉搜索树的变种B+树,在B+树中一个节点可以包含多个值,如下:

4_AgAABe_bA0UPgX_AGX1P750YRUPfdJSf.png

B+树具有以下特性:

  1. 节点中存储的值是有序的。
  2. 若节点包含k个值,则节点下有k + 1个子节点。
  3. 若节点包含k个值,节点内的多个值采用二分法查找其时间复杂度为O(logkN)。
  4. 节点某个值的左子树上所有节点值均小于该值。
  5. 节点某个值的右子树上所有节点值均大于该值。

在InnoDB存储引擎中,一个整型的索引每个节点大约包含1200个索引值,当棵树高是4的时候,就可以存1200的3次方个值,这已经是17亿条数据了。

参考资料: