InnoDB逻辑逻辑存储结构及B树索引

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lu__peng/article/details/80820784

github

1 mysql中的逻辑存储结构

1 表空间

基于InnoDB存储引擎的mysql数据库,存放在数据库中的数据实际上是存放在表空间中的。

对于同一个基于InnoDB存储引擎的mysql实例,这个表空间是所有数据库共享的,存放在数据库中各个表中的数据实际会最终存放在表空间中(mysql以ibdata1表示一个共享表空间);但是如果开启了innodb_file_per_table的话,那么mysql数据会为每一个表创建一个私有表空间(以table_name.idb表示),每一个表中的数据会存放在这个私有表空间中,注意此时共享表空间仍然有用,它此时用于存放Undo信息,系统事物信息等等。

2 段

表空间实际上是有若干个段组成的。

3 区

每创建一个表时,mysql都需要向磁盘申请一定的空间用于数据存储,申请的时候就是以区为单位进行申请的,一个区是由连续的64也组成,以页16k,因此一个区的大小是1M。

4 页

页是mysql进行磁盘管理的最小单位,每页的大小是16k;每次我们查询数据库的时候,实际上是mysql先把存放在磁盘中的数据读入到内存中,然后供我们查找,在这个阶段mysql就是以页为单位进行IO操作的。

5 行

基于InnoDB存储引擎的mysql是以行为单位进行数据存储的。

2 数据结构

2.1 二分查找

数据库中使用最最频繁的操作就是查找了,二分查找算法应该算是最普遍且效率较高(有比他更好的,斐波那契但是也是二分查找的变种)的查找算法,但是应用二分查找需要满足两个条件:1)查找序列有序;2)查找序列是一个顺序表;因此我们可以想办法把存放在mysql数据库中的数据按照顺序存放,查找的时候直接使用二分查找,那么可以极大的节省时间。但是数据库并不仅仅只进行查找操作,虽然查找很频繁但是增删改操作同样是mysql数据库必不可少的操作,如果把数据按照顺序表的数据结构进行有序存放,虽然查找效率很高,但是会影响插入和删除的性能(一个长度为n的有序表,删除第i个数据,那么需要移动n-i为数,效率低下)

此时应该想到链表,因为链表中插入数据和删除数据的时间时间复杂度是O(1),但是很遗憾,基于链表(最普通的那种单链表)的有序表并不能进行二分查找。那么能不能既可以使用二分查找,又不用每次进行修改的时候移动太多数据,有!可以使用树。

2.2 二叉排序树

二叉排序树是一种树型数据结构,它要么是一颗空树,要么是一个仅有根节点的树,要么是一个大于两个节点且,节点的左孩子节点值均小于该节点,右孩子节点值均大于该节点。

另外树型结构是可以使用链表来进行表示的,那么理想状态下(二叉排序树是一颗满二叉排序树),那么对这棵树进行查找的话,使用的就是类似于二分查找的方法,效率很高。而且因为使用链表构建的二叉排序树,在进行插入和删除操作的时候,并不需要移动太多的元素,只需要修改指针指向即可,也是可以接受的。

但是这是理想状态下,实际情况,往往不那么理想,如果构建二叉排序树的一组数据本来就是基本有序的话,那么此时构建的二叉排序树实际上和一条链表无异,此时对二叉排序树进行查找实际上就是对一条链表进行查找,时间复杂度为O(n),往往不能接受这种速度。于是,人们就提出了平衡二叉树。

2.3 平衡二叉树

为了抑制二叉排序树的高度过快增长,人们在二叉排序树的基础上提出了平衡二叉树,平衡二叉树首先是一颗二叉排序树,在二叉排序树的基础上,规定一颗节点的左右子树高度之差绝对值不能大于1.这样可以防止二叉排序树在极端情况下恶化成一条链表。使得二叉树的高度在可控范围内,在进行查找的时候,仍然可以以接近二分查找的速度进行,但是在删除和插入操作的时候需要进行额外的左旋右旋等操作,以维持树的平衡因子,但是这个额外的操作所带来的性能损失是可以接受的。

平衡二叉树的应用很普遍,比如说实现实时排序就可以使用平衡二叉树(我认为跳表实际上也可以看做平衡二叉树的变种,因为它们真的很想,当然跳表也可以实现实时排序)。

但是如果是海量数据的话,现在数据库里往往动不动都是百万行以上数据,如果还以平衡二叉树的数据结构形式组织的话,每个节点仅仅存放一个数据,那么再怎么保证平衡,它的高度也是相当客观的,于是人们想出了在一个节点上存放多个数据,于是就有了B树

2.4 B树和B+树

B树又被称之为B-树,每个非叶节点均存放数据,且可以存放不止一个数据(具体定义可以百度),且节点内部数据有序存放,叶子节点不存放数据,如果查找到叶子节点,则认为查找该组数据中没有目标元素;在对基于B树的一组数进行查询操作的时候(非常大的一组数据),从根节点出发,首先把根节点读入到内存,节点内部的数据有序存放,因此在内存中完全可以利用二分查找进行快速定位,如果找到就返回,如果没有找到,再根据节点指针找对应的下一层节点,再次进行相同的操作,如此反复最终找到目标或者返回这组数中没有该元素。

B+树和B树基本上类似,但是不同的是B+树的非叶节点不存放实际数据,而是仅仅作为一个指向最终目标的索引,实际数据都存放在叶子节点中,且叶子节点从左往右业是有序的,但是每个节点里面也是可以存放不止一个元素,且节点内元素有序存放。在进行查找的时候,根据非叶节点,找到最终叶子节点,比较,满足条件则返回数据,否则,该组数据中没有目标元素。

3 B树索引

基于InnoDB存储引擎的mysql数据库支持B树索引,B树索引实际上是B+树数据结构的一种实现。为什么没有用B树数据结构呢?因为B树中的节点中存放的是实际数据,而InnoDB是面向行存储的,一行记录可能非常大,如果将行记录直接存放在节点中的话,一次读入的数据就会减少,因此没有使用B树;使用B+树,节点中仅仅存放索引,并且把一个节点设计成一页,那么一页就能够存放更多的索引,在进行查找的时候,可以减少IO次数。

基于InnoDB的B树索引,索引列按照B+树数据结构顺序存放,最终的叶子节点指向最终记录所在的页所在的叶子节点。在进行查找操作的时候:

  • 把索引的根节点读入到内存中,对节点内部的数据按照二分查找快速定位(1次IO)
  • 读入下一层的对应节点,再次同样快速定位(1次IO)
  • 找到对应也在节点,读入,同样快速定位(n次IO) 这颗B+树有多高就进行多少次IO,资料上说往往都是2-3层

我们试想一下,有一个1000w行记录的数据,每行1k,大概10G大小,每页16k,那么大概需要640页,如果不用索引,我们进行查找的话,就需要遍历,读入第一页…最终找到目标,极端情况下,需要读入640页,也就是说要进行640次IO。

根据主键建立B+树索引,每页可以存放256个索引,那么建立一个三层的B+树即可(256+256^2+256^3>1000w),因此按照索引进行查找的时候,:

  • 把索引根节点所在页读入内存 (1次IO)
  • 读入第二层某个节点页 (2次IO)
  • 找到叶子节点 (3次IO)

比较两者,是不是发现,建立索引之后,查询的效率大大提高了,但是需要主要的是,索引是实实在在存在的,也存放在磁盘中,需要的时候读入即可,如果一张表数据量很少,一次IO就可以把数据页全部读入到内存中,那么此时再建立索引就有点得不偿失了,因为此时需要2次IO。

因此建立索引不要盲目,根据国情出发。

参考:mysql技术内幕:InnoD存储引擎

猜你喜欢

转载自blog.csdn.net/lu__peng/article/details/80820784
今日推荐