浅谈数据库文件存储原理

数据库文件存储原理

在说数据库存储原理之前,我们不妨先思考个问题。假如我们是数据库存储引擎的设计者,我们会如何在磁盘上存储数据。当然存储引擎是个很复杂的东西,但我们可以简单的思考数据的存储方式,用什么样的数据结构比价合适,合理,性能最好。

我们学过很多数据结构:集合,数组,链表,hash表,树,堆,栈等等等等。它们是我们存储数据的手段。之前学习Python的时候,有本书叫做《Python学习手册》刚开始它就让我们学习实现写一个数据库。刚开始用元祖,列表这些简单的数据结构,后面用到复杂的字典类型。我们发现,数据结构是我们用来存储数据的手段,使得我们存取数据更加高效。所以学会各种数据结构的特性后我们遇到数据存储的问题就有了若干选项。

我们熟知的关系型数据库和NoSql数据库底层实现都是树,mysql使用的B+树,mongodb用的是B树。

为什么不用数据组,链表,hash表能?我想最初设计者应该也做过选择吧。

数组:

Java中ArrayList是用数组实现的,当插入的数据超过初始值的时候,数组就要重新定义一个新的更长的数组来“装”,而且随机插入一个数据时间复杂度是O(n),性能太差,且数组只能存单一类型的元素,因此无法满足。

链表 

也有缺陷虽然它的随机插入时间复杂度是O(1)但随机访问的时间复杂度是O(n),也是无法满足。

Hash表 

会是比较好一点的选择,我们选择适合的数据结构,其实都是为了避免全量IO,我们知道,每次IO请求都涉及到用户态和内核态的上下文切换,会有很大的资源消耗,而全量IO是最糟糕的情况。

所以时间复杂度越高的IO的次数就越多。所以为了避免全量IO我们考虑使用更复杂点的数据结构。Hash表我们都熟悉,Java中的HashMap就是由hash表来实现的。它的组成是一个数组的hash槽,每个槽位上是一个链表(JDK1.8及以后版本对其做了优化),我们将数据hash值取余的方式(最开始是这样的,hashmap中的实现负载一点)均匀的放在各个槽位的链表上,余数相等的hash碰撞,放在同一链表。虽然Hash表一般情况下能避免全量IO,但是它用到了链表来放数据,极端情情况下它也会有全量IO。

二叉树 

说到二叉树,不难想到递归这个反人类的算法。它的特点是:

  1. 左子树小于根节点,根节点小于右子树(这里比较的是值,比如hash)
  2. 非叶子节点最多有2个孩子节点

由于它的特性,在极端情况下它会变成一个链表,时间复杂度是O(n),所以它不适合。

平衡二叉树 

AVL树

基于AVL算法

  1. 任何节点的左右子树相差都不能超过1
  2. 当然它也属于二叉树,所以拥有二叉树的特点

插入或者删除节点通过1所说的平衡因子来对树进行旋转,来达到平衡。虽然这样解决了变成单链表的极端情况,但这是以牺牲时间为代价的。每次旋转都需消耗时间。数据库这种大量存入数据,频繁查询修改的场景可想而知有多耗时,所以它也不适合。

红黑树

在AVL树的基础上做的优化,用特殊的方法使其减少旋转次数,从而节省时间。它有以下特性:

  1. 根节点是黑色节点
  2. 非根节点非红即黑
  3. 红色节点的两个子节点是黑色(从根节点到叶子节点的每条单路径上没有两个重复的红色节点)
  4. 叶子节点必为黑色节点(NUIL节点)
  5. 任意节点到叶子节点的所有路径包含的黑色节点数目都相同

我们插入或者删除数据时对红黑树的节点做了操作,打破了以上特性,红黑树通过旋转来维持这些特性。节点的删除和增加算法这里就先不说了。数据库大量存储数据的场景,用红黑树的话势必这棵树会很深,很深就意味着时间复杂度很高,而且还要旋转消耗时间。所以它也不是我们好的选择。

 

B树

终于说到正题了,我们先来给B-Tree正名,很多人将它翻译成B-树,因为有个B+树,其实这样是不对的。正确的翻译应是B树,中间的那个“-”是我们平时说的“杠”不是减,它是个连字符(hyphen),所以不是B减树。我不知道别人怎么样,反正我刚开始是这么认为的。所以这个坑就填了吧。

说到B树和B+树,其实并不神秘,因为他们都是从二叉树转化而来的,就是个多路平衡查找树,就是孩子多了点而已。下面看看B树的特性:

假设有一个m阶的B树

1.每个节点最多拥有m-1个key

2.根节点key的个数[1,m-1]

再多就分裂了(insert一个7):

 3.根节点子节点数[2,m] 

4. 所有节点的子节点数最大的数值是多少就称为多少阶

 现在根节点,而且根节点的key已经是m-1=4了,而且已经有5个孩子了如果我们再插入一个18会发生什么

是的它是5阶的,所以它不能再分裂出一个孩子,只能增加深度了。

 5.树里面的关键字都是严格按从小到大排序的,

6.除根节点和叶子节点外其他节点的都至少有m/2(向上取整)个子节点

7.所有叶子节点都位于同一层

为了方便我用个3阶的

再输入一个比13大的key就要分裂增加深度了,那么1和9这两个子树会怎么样?

插入节点过程:

过程是将遍历到13发现14比13大所以加到13后面,发现这个子树最多容纳m-1就是2个key,所以进行了分裂,13被放到根节点,子节点分裂成12和14两个子节点。然后根节点也经历了相同的操作,分裂成出两个子节点7和13,7>1&7<9所以1和9变成7的子树,同理13.

删除节点过程:

删除14这个节点,13这个节点就不满足除根节点和叶子节点外其他节点至少拥有m/2(cil)个子节点且13最大如果归并根节点那么就违反了右边的子节点12要比根节点大的原则,所以只能归并到12的右边。

但如果是这样,根节点左右两边深度不一样,违反了叶子节点都在同一层的原则。

于是会将7这个节点与根节点合并来降低左边子树的层数使左右相等。

最终的结果是:

推荐上面我用的这个工具,有插入删除的动画效果 ,还可以看其他数据结构。好吧这就是B树

B+树

 B+树是B-树的一种进化,所以它具有B-树的特点。下面看看它独有的特性

  • 有k个子结点的结点必然有k个关键码;
  • 非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中。
  • 树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录。
  • 每个节点都会出现在

节点查询

查询数字3:

第一次I/O

第二次I/O 

 第三次I/O

B+树在非叶子节点上面不放数据,只放索引

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值