本文主要介绍MySQL的InnoDB搜索引擎的索引结构。文章从最初的全文检索开始,经过Hash、二叉树、平衡二叉树、B树到最后为什么选用B+树作为索引结构的路线介绍。文章涉及数据结构以及一些硬件知识,全文较长请耐心阅读。
介绍之前分享一个学习数据结构的优秀网站 Data Structure Visualizations 里面提供了各种数据结构的动画演示,打开网站配合文章你会得到惊奇的效果。
目录
全文检索
这种方式是在MySQL查询时候全盘扫描。相当于一个大型图书馆里面的书是杂乱无章的,想要查找一本书的时候需要从头到尾的进行查询一遍。这种是效率最低的方式时间复杂度为O(n)。为了优化增删改查速度再此基础上可以优化成Hash结构。
Hash结构
为了弥补全文检索的缺点进一步优化索引结构为Hash结构,这种结构可以通过Hash算法把要存储的内容计算得出地址。在查找的时候可以同样利用Hash算法计算得出的地址取出内容。Hash数据结构增删改查时间复杂度为O(1)。数据结构图如下所示,存储和取值可以在Hash动画演示中操作。
Hash结构的优点在于对单一值的存储和查询比较快,但是我们都知道使用MySQL进行查询很多的时候是进行的一个范围的查找,对于这种情况的查询Hash结构的时间复杂度从O(1)直接提高到了O(n)。所以MySQL的索引结构就演变成了树结构。树结构分为二叉树、二插平衡树(AVL树)、B树、B+树、红黑树等等。增、删、改、查、排序和范围的时间复杂度为O(log2(n)。
二叉树
所谓的二叉树就是指的每个结点最多有两个子树的树结构。二叉树概念介绍 有需要的点击查看,本文重点介绍实战内容概念性的不加以赘述。二叉树结构图如下图,动态操作可以在二叉树动画演示中模拟。
二叉树主要的特点:
- 一个节点只能有两个子节点,一个节点度不能超过2;
- 左子节点小于本节点,右字节点大于本节点;
检索原理:对于此二叉树的查找发现,查找深度为1的次数为1、查找深度为2的次数为2、深度为N的查找次数为N;因此此树的平均查找次数为(1+2+2+3+3+3)/6 = 2.3(次)。
通过上述的检索原理可知,二叉树有2个缺点。当二叉树中每次新插入的值都比原树中的节点值大或者小就会形成下图的右倾或者左倾的树形结构从树形结构变成了链表。
这种左倾或者右倾的二叉树在查找的时候时间复杂度从O(log2(n))变成了O(n)。
第二个缺点是当出现下面的情况的时候,数据结构变成了链表+树的结构对于增删改查等操作又增加了困难。
鉴于以上问题对二叉树有进一步的进行了优化从二叉树变成了平衡二叉树。
平衡二叉树(AVL树)
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等。为了防止想二叉树一样的左倾或者右倾的情况发生,平衡二叉树引入了旋转操作其中包括左旋和右旋,二叉树的结构图如图所示,演示动画可以在平衡二叉树演示模拟。
平衡二叉树有一个缺点就是随着数据量增大树的高度也随之变高。对树进行操作的时候增加了IO的次数同样会变慢。为了解决这个问题就需要增加的树的节点降低树的高度从“瘦高”变成“矮胖”从而减少磁盘的IO次数,继而引入的B树。
B树
B树又称B-树、二三树。 B树的结构图如图所示,树的增删改查操作可以用B树的动画模拟动画模拟。
了解完B树的数据结构之后我们来了解一下B树的检索原理:首先数据库的索引是存在磁盘上的,如果数据量很大的情况下数据库的索引也会很大,可能超过几个G。当我们利用索引查询数据的时候是不可能一次加载所有的索引进内存,计算机只能逐一加载每一个磁盘页(这里引入枫旸一生博主的一篇文章介绍硬件),因为磁盘页是对应着索引树的每一个节点。Innodb默认磁盘页大小16K,Innodb每次加载数据都是以页为单位。sql语句查看页大小SHOW GLOBAL STATUS LIKE 'Innodb_page_size';
接下来我们了解一下B树的检索原理:
每一个节点占用一块磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在的磁盘块的地址。
模拟查找关键字29的过程:
根据根节点找到磁盘块1,读入内存。【磁盘 I/O操作第1次】
比较关键字29在区间(17,35)找到磁盘块1的指针P2.
根据P2指针找到磁盘块3,读入内存。【磁盘 I/O操作第2次】
比较关键字29在区间(26,30),找到磁盘块3的指针P2。
根据P2指针找到磁盘块8,读入内存。【磁盘 I/O操作第3次】
在磁盘块8中的关键字列表找到关键字29。
分析上面过程,发现需要3的磁盘的I/O操作和3次的内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。I/O操作是影响整个BTree查找效率的决定因素。BTree相对于AVL树缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。其实到B树的时候已经优化的很好了,可是这群“变态”又进行了优化引入了B+树。
B+树
B+树的结构如图:
插入的动画演示地址B+树动画演示。
我们可以看到图中的所有Data信息都移动到叶子节点中,而且节点和节点之间都会有一个指针指向,这个也是B+树的核心点。B+树相当于树形结构+链表,把各种数据结构的优点整合(可以类比JAVA8版本的HashMap的数组+链表+红黑树)这样可以大大的提高范围查询 效率,也方便遍历整个树。
B+树特点总结为:
- 非叶子节点不在存储数据,数据只存储在同一层的叶子节点上;
- 叶子之间增加了链表,获取所有节点不再需要中序遍历
说完特点和结构之后再聊聊B+树的检索原理,由于B+树的非叶子节点智慧存储键值信息,假设每个磁盘块能存储4个键值及指针信息,则变成B+树后其结构如下图所示:
B树结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而且每一页的存储空间是有限的,如果data数据较大时会导致每个节点(即每一页)能存储的key的数量很小,当存储的数据量很大时会导致B树的深度较大,增大查询时的磁盘I/O次数进而影响查询效率。
但在B+树中,所有的数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点只存储key值信息,这样可以加大每个节点存储的key值数量,降低B+树的高度。
1、InnoDB存储引擎的最小存储单元是页,页可以用户存放数据也可以用于存放键值+指针;
2、索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,首先找到跟页继而在去数据页中查找到需要的数据;
B+树算法:通过继承了B树的特征,B+树相比B树,新增叶子节点与非叶子节点的关系。叶子节点中包含数据和键值,非叶子节点中值包含键值和子节点引用,不包含数据。通过非叶子节点查询叶子节点获取对应的数据,所有相邻的叶子节点包含非叶子节点使用链表进行结合,叶子节点是顺序排序并且相邻节点有顺序引用的关系。
以上就是个人对InnoDB中B+树索引数据结构以及演变过程的介绍,欢迎批评指正。MySQL的搜索引擎还有MyISAM搜索引擎本文不作为重点介绍。喜欢的朋友可以关注,后续会更新数据库事务、锁等技术。