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

server/2025/1/18 0:06:15/

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

  • DataManager
    • AbstractCache 引用计数缓存框架
    • DataItem
    • DataManager
    • PageCache
    • Logger 日志记录
  • 代码结构
  • 参考资料

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缓存都是用这个缓存模块做的。

整个使用缓存的过程即为

java">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)


http://www.ppmy.cn/server/159210.html

相关文章

滚动字幕视频怎么制作

在当今的视频创作领域,滚动字幕被广泛应用于各种场景,为视频增添丰富的信息展示和独特的视觉效果。无论是影视剧中的片尾字幕、新闻节目中的资讯滚动,还是综艺节目中的人员与鸣谢信息展示,滚动字幕都发挥着不可或缺的作用。接下来…

Python剪辑视频小妙招(moivepy库)

起因 最近一直在b站上投稿喜羊羊与灰太狼的视频,但是苦于需要手动裁剪视频的片头和片尾,裁剪的多了就发现喜羊羊与灰太狼的视频片头几乎都是1分25秒结束,也就是持续85秒,片尾也差不多是持续1分02秒差不多也就是62秒,于…

effective-Objective-C 第二章阅读笔记

对象,消息,运行期 文章目录 对象,消息,运行期前言理解“属性”这一概念属性修饰符原子性nonatimicatomic 读/写权限内存管理语义方法名 自定义初始化方法小结 在对象内部尽量直接访问实例变量小结 对象等同性特定类的isEqual执行深…

【Vue3 入门到实战】5. Watch 监视

目录 1. 监听ref定义的数据 1.1 监视ref定义的基本类型数据 1.2 监视ref定义的引用类型 1.2.1 修改属性 1.2.2 修改整个对象 2. 监视reactive定义的数据 3. 监视ref 和 reactive定义的对象类型中的某个属性 3.1 属性值为基本类型 3.2 属性值为引用类型 4. 监视上述…

MySQL的索引

一、索引概述: 索引(index)是帮助MySQL高效获取数据的数据结构(有序) 优缺点: 优点:提高数据检索,降低数据库的IO成本,通过索引列对数据库进行排序,降低数据排…

分类统计字符个数(PTA)C语言

本题要求实现一个函数,统计给定字符串中英文字母、空格或回车、数字字符和其他字符的个数。 函数接口定义: void StringCount( char s[] ); 其中 char s[] 是用户传入的字符串。函数StringCount须在一行内按照 letter 英文字母个数, blank 空格或回…

隧道IP广播与紧急电话系统:提升隧道安全的关键技术

隧道IP广播与紧急电话系统:提升隧道安全的关键技术 随着现代城市交通的迅猛发展,隧道作为重要的交通基础设施,其安全性与应急处理能力显得尤为重要。隧道IP广播与紧急电话系统作为保障隧道安全的关键技术,正发挥着越来越重要的作…

代码随想录算法训练营第三十天-贪心算法-763. 划分字母区间

标记字符最远位置,这是人能想到的?定义一个26个字母的数组,下标表示字母的位置,数组值表示当前字母在字符串中遍历过程中所处的位置算法题目无厘头太多,但解法也是太精彩,可是根本记不住,要每日…