【MyDB】3-DataManager数据管理 之 0-DataManager数据管理框架

DataManager

继事务管理后,来到了myDB的核心。数据管理DataManager

DataManager 继承了一个引用计数缓存框架AbstractCache(可见1-引用计数缓存框架),操控的资源为DataItem

AbstractCache 引用计数缓存框架

redis是基于LRU最近最久未使用的缓存框架。

但是LRU有个问题,上层模块无法感知到资源的驱逐操作。因此会带来不必要的写入

因此,在这里使用引用计数缓存框架。

DataItem和Page两个资源都是基于引用计数缓存框架来获取和释放。

核心思路在于:只有当上层未引用该资源时,才会释放资源。

可以看到,使用的是一个两级缓存框架。

  • 第一级是page缓存,在内存维持page,实质是byte[],每个page都有一个引用数p,每次引用page,p++,释放page,p–。一旦p==0,就把page写回磁盘(page脏时),在内存中释放page。

  • 第二级缓存是dataItem缓存,这里的dataItem实质上是切片(切片是go的一种内置结构)对page中一段字节数组的引用,并没有实质上的缓存。这里同样是引用计数法,只不过,当p==0时,只是把切片释放了而已。

在这里作者用了一个很漂亮的写法,独立写了一个引用计数法的缓存模块,把缓存的对象和get缓存和release缓存的接口留了出来,所以这里的page缓存和dataItem缓存都是用这个缓存模块做的。

整个使用缓存的过程即为

1.查找dataItem(简写为d)-->
2.dataItem缓存存在d,直接返回d,不存在d时,在dataItem缓存中创建d缓存-->
3.创建d缓存会从其所在的page(简写为p)中创建切片,若page缓存中不存在p,创建p缓存
4.创建p缓存,即从磁盘中写出相应的page

在这里插入图片描述

一个缓存要能维持在内存中,那么它的引用数就必须一直满足>=1,且不能中断,即是每时每刻都有对缓存的引用,这就意味着,如果对数据库的访问频率不够高,那么引用计数法写的缓存是无效的。可想而知,用引用计数法写的缓存一般比用LRU写的缓存,缓存数不够。
所以在p–的时候可以考虑异步延迟,延迟时间由当前缓存数量决定。

其次在这里我认为用引用计数法写page缓存并不是一个好选择,因为假设有10%的数据(访问频率够高)一直被缓存,就意味着,那10%数据对应的page也会一直维持在内存。如果这10%的数据分步在所有的page中,那么所有的page都会维持在缓存,那所谓的分页管理就没有意义了。

用引用计数法写可以很容易把锁表和缓存结合,在实现诸如b-link-tree之类需要节点加读写锁的索引结构时很方便。

DataItem

DataManager是通过对DataItem数据项的修改来实现对底层实际数据页的操作。

DataItem将底层对文件的操作封装起来,向上层DataManager提供一个数据操作的接口。

因此DataManager只需要操控数据项DataItem即可。

每个DataItem都有唯一的uid来标识。uid=页号 offset(前16位为页号pgno,后16位为页内偏移offset)

DataManager

介绍了DataItem后,DataManager主要就有两部分的功能。

一是初始化(从零初始化或者打开现有的文件初始化),这里并不重要,后续会提

另一个就是对DataItem的操作。

  • 从页面中获取DataItem read(uid)。根据DataItem的uid从PageCache中读取到具体的DataItem数据项。

    uid=pgno + offset(数据项在pgno中的偏移位置)

    因此,将uid解析为pgno和offset

    根据offset定位到dataitem项的位置,先读取isValid以及size,之后根据读取到的size读取所有data

    将读取到的 isValid,size,data封装为DataItem返回

  • 向页面中插入data insert(xie,byte[] data) .将上层传入的byte[] data 包装成DataItem,然后写入页面中

    data包装为DataItem项

    根据pageIndex 页面索引类查找空闲页面

    将DataItem写入空闲页面,得到插入的位置offset

    将插入操作写入日志

    返回uid(Types.addressToUid(pgno,offset)接收pgno和offset,拼成uid)

    最后取出的pg重新插入PageIndex中

PageCache

继承AbstractCache

提供对页面的缓存操作。

页面的话分为首页和普通页。普通页就正常存数据,首页则为确保安全,主要功能为启动检查,检查上次是否正常关闭

MYDB 的第一页,只是用来做启动检查。具体的原理是,在每次数据库启动时,会生成一串随机字节,存储在 100 ~ 107 字节。在数据库正常关闭时,会将这串字节,拷贝到第一页的 108 ~ 115 字节。

这样数据库在每次启动时,就会检查第一页两处的字节是否相同,以此来判断上一次是否正常关闭。如果是异常关闭,就需要执行数据的恢复流程。

普通页存数据的结构则为:[FreeSpaceOffset] [Data] 前两个字节存储本页空闲空间

Logger 日志记录

通过DataItem插入数据时,考虑到数据库的持久性,需要在故障时恢复数据。

因此,对于插入,更新等操作都需要写入日志中。便于故障时根据日志来恢复

代码结构

DataManager核心文件如下。接下来将就以下的具体实现来一步步的讲解整个DataManager的结构。

在这里插入图片描述

在这里插入图片描述

参考资料

关于用引用计数法写缓存的一点看法

MYDB 2. 引用计数缓存框架和共享内存数组 | 信也のブログ (shinya.click)

数据管理 | EasyDB (blockcloth.cn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值