一、概览

1.steal/no-force策略

lab6要实现的是simpledb的日志系统,以支持回滚和崩溃恢复;在lab4事务中,我们并没有考虑事务执行过程中,如果机器故障或者停电了数据丢失的问题,bufferpool采用的是no-steal/force的策略,而这个实验我们实现的是steal/no-force策略,两种策略的区别如下:

  1. steal/no-steal: 是否允许一个uncommitted的事务将修改更新到磁盘,如果是steal策略,那么此时磁盘上就可能包含uncommitted的数据,因此系统需要记录undo log,以防事务abort时进行回滚(roll-back)。如果是no steal策略,就表示磁盘上不会存在uncommitted数据,因此无需回滚操作,也就无需记录undo log。
  2. force/no-force:force策略表示事务在committed之后必须将所有更新立刻持久化到磁盘,这样会导致磁盘发生很多小的写操作(更可能是随机写)。no-force表示事务在committed之后可以不立即持久化到磁盘, 这样可以缓存很多的更新批量持久化到磁盘,这样可以降低磁盘操作次数(提升顺序写),但是如果committed之后发生crash,那么此时已经committed的事务数据将会丢失(因为还没有持久化到磁盘),因此系统需要记录redo log,在系统重启时候进行前滚(roll-forward)操作。

2.redo log与undo log

为了支持steal/no-force策略,即我们可以将未提交事务的数据更新到磁盘,也不必在事务提交时就一定将修改的数据刷入磁盘,我们需要用日志来记录一些修改的行为。在simpledb中,日志不区分redo log和undo log,格式较为简单,也不会记录事务执行过程中对记录的具体修改行为。

对于redo log,为确保事务的持久性,redo log需要事务操作的变化,simpledb中用UPDATE格式的日志来保存数据的变化,在每次将数据页写入磁盘前需要用logWrite方法来记录变化:

写入磁盘前:

这样,对于这些脏页,即使断电丢失数据了,我们也可以通过事务id来判断事务是否已经提交(这里提交事务会记录另一种格式的日志),如果事务已经提交,则重启时根据日志的内容就可以把数据恢复了;总而言之,通过这样的方式,可以让simpledb支持崩溃恢复;

对于undo log,我们采用的是对heappage中保存一份旧数据:

数据页一开始的旧数据是空的,那什么时候会对旧数据进行更新呢?答案是事务提交时,当事务提交时,就意味着这个修改已经是持久化到磁盘了,新的事务修改后就数据页的数据就是脏数据了,而在新事务回滚时,由于我们采用的是steal策略,脏页可能已经在页面淘汰时被写入磁盘中了,那么该如何进行恢复呢?答案是before-image,即oldData,通过上一次成功事务的数据,我们可以恢复到事务开始前的样子,这样,就可以实现了事务的回滚了。

3.日志格式与checkpoint

在实验开始前应该理清楚几种日志的格式,对于后面写代码很重要,格式说明在LogFile的注释中有;

The format of the log file is as follows:

  • The first long integer of the file represents the offset of the last written checkpoint, or -1 if there are no checkpoints
  • All additional data in the log consists of log records. Log records are variable length.
  • Each log record begins with an integer type and a long integer transaction id.
  • Each log record ends with a long integer file offset representing the position in the log file where the record began.
  • There are five record types: ABORT, COMMIT, UPDATE, BEGIN, and CHECKPOINT
  • ABORT, COMMIT, and BEGIN records contain no additional data
  • UPDATE RECORDS consist of two entries, a before image and an after image. These images are serialized Page objects, and can be
    accessed with the LogFile.readPageData() and LogFile.writePageData() methods. See LogFile.print() for an example.
  • CHECKPOINT records consist of active transactions at the time the checkpoint was taken and their first log record on disk. The format of the record is an integer count of the number of transactions, as well as a long integer transaction id and a long integer first record offset for each active transaction.

简单来说,simpledb的日志记录一共有5种:ABORT, COMMIT, UPDATE, BEGIN, and CHECKPOINT,分别记录事务失败、事务提交、写入磁盘前的脏页、事务开始、检测点,这些格式的日志都记录在同一个日志文件中;日志文件以及每条日志的通用格式如下:

对于ABORT, COMMIT, and BEGIN这三种,中间的content是空的;对于UPDATE格式的记录,有两部分组成,即before image和after image,分别记录修改前和修改后的日志;事务提交失败回滚我们会用到before image,事务提交成功但数据由于故障丢失数据我们会用到after image;对于CHECKPOINT 记录,主要记录在checkpoint点活跃的事务数,以及每个事务的的事务id和第一条日志记录的偏移量;

其中checkpoint可以说是整个日志文件的核心,在崩溃恢复时很有用;在崩溃恢复时,我们会读取到checkpoint所在的位置,在checkpoint之前的修改已经是刷入磁盘的,除非磁盘坏了否则就是永久不会丢失的;对于checkpoint之后的日志,我们只保证修改持久化到日志,但未保证将日志记录的内容持久化到磁盘,因此崩溃恢复时,我们需要从checkpoint开始往后读,然后根据日志记录进行恢复。

二、实验过程

该实验主要分为两部分:rollback和recovery

1.Rollback

rollback是undo log做的事,即提供上一个版本的快照(相比MVCC真是微不足道),在回滚时将上一个版本的数据写回磁盘,思路比较简单:

1.根据tidToFirstLogRecord获取该事务第一条记录的位置;

2.移动到日志开始的地方;

3.根据日志格式进行读取日志记录,读到update格式的记录时根据事务id判断是否为要修改的日志,如果是,写before image

代码如下:

 public void rollback(TransactionId tid)throws NoSuchElementException, IOException {synchronized (Database.getBufferPool()) {synchronized(this) {preAppend();// some code goes hereLong firstLogRecord = tidToFirstLogRecord.get(tid.getId());//移动到日志开始的地方raf.seek(firstLogRecord);Set<PageId> set = new HashSet<>();while (true) {try {//Each log record begins with an integer type and a long integer//transaction id.int type = raf.readInt();long txid = raf.readLong();switch (type) {case UPDATE_RECORD ://UPDATE RECORDS consist of two entries, a before image and an//after image.  These images are serialized Page objects, and can be//accessed with the LogFile.readPageData() and LogFile.writePageData()//methods.  See LogFile.print() for an example.Page beforeImage = readPageData(raf);Page afterImage = readPageData(raf);PageId pageId = beforeImage.getId();if (txid == tid.getId() && !set.contains(pageId)) {set.add(pageId);Database.getBufferPool().discardPage(pageId);Database.getCatalog().getDatabaseFile(pageId.getTableId()).writePage(beforeImage);}break;case CHECKPOINT_RECORD://CHECKPOINT records consist of active transactions at the time//the checkpoint was taken and their first log record on disk.  The format//of the record is an integer count of the number of transactions, as well//as a long integer transaction id and a long integer first record offset//for each active transaction.int txCnt = raf.readInt();while (txCnt -- > 0) {raf.readLong();raf.readLong();}break;default://othersbreak;}//Each log record ends with a long integer file offset representing the position in the log file where the record began.raf.readLong();} catch (EOFException e) {break;}}}}}

写这个exercise的时候要注意之前buffer pool也要进行相应的修改,一部分是刷入磁盘前需要记录:

另外就是提交事务前需要将更新before image:

由于这里获取不到脏页,我们可以在flushPages中修改:

2.Recovery

崩溃恢复是redo log要做的事,在因故障数据丢失时,有部分数据是还未写入数据库的,这个时候可以利用到undo log。从日志文件中,我们可以获取到checkpoint所在位置,然后对checkpoint后面的日志记录进行读取并进行恢复数据。

1.对于未提交的事务:使用before-image对其进行恢复;

2.对于已提交的事务:使用after-image对其进行恢复;

具体实现代码如下:

 public void recover() throws IOException {synchronized (Database.getBufferPool()) {synchronized (this) {recoveryUndecided = false;// some code goes hereraf = new RandomAccessFile(logFile, "rw");//已提交的事务id集合Set<Long> committedId = new HashSet<>();//存放事务id对应的beforePage和afterPageMap<Long, List<Page>> beforePages = new HashMap<>();Map<Long, List<Page>> afterPages = new HashMap<>();//获取checkpointLong checkpoint = raf.readLong();if (checkpoint != -1) {//                    raf.seek(checkpoint);}while (true) {try {int type = raf.readInt();long txid = raf.readLong();switch (type) {case UPDATE_RECORD:Page beforeImage = readPageData(raf);Page afterImage = readPageData(raf);List<Page> l1 = beforePages.getOrDefault(txid, new ArrayList<>());l1.add(beforeImage);beforePages.put(txid, l1);List<Page> l2 = afterPages.getOrDefault(txid, new ArrayList<>());l2.add(afterImage);afterPages.put(txid, l2);break;case COMMIT_RECORD:committedId.add(txid);break;case CHECKPOINT_RECORD:int numTxs = raf.readInt();while (numTxs -- > 0) {raf.readLong();raf.readLong();}break;default:break;}//endraf.readLong();} catch (EOFException e) {break;}}//处理未提交事务,直接写before-imagefor (long txid :beforePages.keySet()) {if (!committedId.contains(txid)) {List<Page> pages = beforePages.get(txid);for (Page p : pages) {Database.getCatalog().getDatabaseFile(p.getId().getTableId()).writePage(p);}}}//处理已提交事务,直接写after-imagefor (long txid : committedId) {if (afterPages.containsKey(txid)) {List<Page> pages = afterPages.get(txid);for (Page page : pages) {Database.getCatalog().getDatabaseFile(page.getId().getTableId()).writePage(page);}}}}}}

三、实验总结

通过本次实验,实现了simpledb的日志系统,也学习了部分redo log,undo log,checkpoint等相关的知识。在steal/no-force策略下实现的bufferpool,要想支持事务,就要用到日志,其中redo log用于故障恢复,undo log用于事务回滚。相比于lab4在不考虑故障数据丢失的情况下,lab6可以说是lab4事务的补充,通过日志系统,就可以实现真正的持久性,以及更加高效的buffer pool(不用在每次事务提交时频繁将数据写入磁盘中)。这也是6.830的最后一个实验,6个实验下来,收获颇多,一个简单的数据库系统就这样一步一步构造出来了,期间对于学习MySQL的原理也有很大的帮助,感谢MIT开源这么好的实验!!!在后面有时间,也想尝试下6.824的实验(6.824的实验相对6.830就更恐怖了,不是一个级别了)。后续有时间也要对整个simple db做一个总结。

实验时间:2021.11.09-2021.11.13

实验报告撰写时间:2021.11.13

MIT6.830 lab6 Rollback and Recovery 实验报告相关推荐

  1. MIT6.830 lab5 B+ Tree Index 实验报告

    一.实验概览 lab5主要是实现B+树索引,主要有查询.插入.删除等功能,查询主要根据B+树的特性去递归查找即可,插入要考虑节点的分裂(节点tuples满的时候),删除要考虑节点内元素的重新分配(当一 ...

  2. MIT6.830实现关系型数据库simpleDB

    lab1 实现数据管理 主要需要实现存储.访问与管理物理层面的数据(二进制文件),以及将其映射为逻辑层面的数据(关系表).在这一课题的最后,还要求实现SimpleDB中最基本的操作--SeqScan, ...

  3. 数据库原理实验二 数据库管理 实验报告

    广州大学学生实验报告实验二:数据库管理 此篇分享仅供参考学习,图文禁复制,勿作他用!谢谢配合! 数据库原理实验之实验二:数据库管理 软件:Oracle SQL Developer 今天实验才刚开始,花 ...

  4. 思科模拟器划分子网实验报告_模拟多子网群集以设置SQL Server Always On可用性组–实验设置

    思科模拟器划分子网实验报告 In this article, we are going to see how to create a multi subnet cluster spanning acr ...

  5. ucoreOS_lab8 实验报告

    所有的实验报告将会在 Github 同步更新,更多内容请移步至Github:https://github.com/AngelKitty/review_the_national_post-graduat ...

  6. 北邮数据库实验7mysql_北邮大三下第7次数据库实验报告--mysql

    实验七数据库的事务创建与运行实验一.实验内容 1.定义三种模式的数据库事务 2.察看事务的隔离级别 二.实验要求 1.用SQL语句完成以上操作 2.要求学生独立完成以上内容. 3.实验完成后完成要求的 ...

  7. 计算机基础数据表示实验,2018大学计算机基础上机实验报告

    <2018大学计算机基础上机实验报告>由会员分享,可在线阅读,更多相关<2018大学计算机基础上机实验报告(15页珍藏版)>请在人人文库网上搜索. 1.实验(一)1:计算机硬件 ...

  8. 计算机取证的相关案例,计算机取证实验报告(共9篇).doc

    计算机取证实验报告(共9篇) 计算机取证实验报告(共9篇) 计算机取证实验报告 <计算机取证技术>实验报告 实验一 实验题目: 用应急工具箱收集易失性数据 实验目的: (1)会创建应急工具 ...

  9. SQL触发器总结 - sql server 2012数据库基础-触发器及应用-实验报告

    这是我大三第一学期<数据库基础>这门课的实验报告,总共15个实验,我挑了几个比较重要的放在博客上,方便查看和复习,尤其是最近SSM后端开发,希望对大家,以及将来的我有所帮助! 实验要求 在 ...

最新文章

  1. dedecms mysql 支持_安装dedecms MySQL 支持 不支持无法使用本系统 GD 支持Off解决办法...
  2. SAP ABAP Netweaver Authorization trace tool - SAP ABAP 权限跟踪监控工具
  3. Bash脚本教程之字符串操作
  4. ios 查询mysql数据库操作系统_iOS数据库FMDB--增删改查(模糊查询)详细介绍
  5. 每天进步一点点《ML - K-means》
  6. 第一次作业:基于Linux进程模型分析
  7. linux 查看手机硬件信息失败,linux下硬件信息的查看总结
  8. 基于JAVA+SpringMVC+Mybatis+MYSQL的电影院订票系统
  9. SVN版控系统的安装和使用
  10. Android_Bitmap_图片的二次采样并生成缩略图
  11. 全向移动机器人参数校准对比及流程分析
  12. html字体加粗代码_9102年了,公众号还不会换字体?
  13. matlab 非线性辨识,非线性系统辨识Matlab实现
  14. 医疗图像配准-点云配准总结
  15. Vue+canvas 实现自定义文字样式转图片,文字与图片进行合成(内蒙古民族大学实习项目)
  16. QOpenGLWidget显示图片
  17. 把数组排成最小的数(剑指offer第33题)
  18. 关于iexplore.exe占用CPU100%的问题
  19. hpml510服务器做系统,HPJetdirect打印服务器(300X510X)管理员指南.PDF
  20. python爬虫运行时电脑无响应_Python爬虫爬资源时由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。怎么破?...

热门文章

  1. Linux中Docker常用命令
  2. linux用shell写正则表达式,Linux命令行与Shell脚本编程大全-shell正则表达式
  3. 服务器低功耗cpu性能,节能省电家庭共享 7款低功耗处理器推荐
  4. java 集群会话管理_架构设计之Spring-Session分布式集群会话管理
  5. mysql blob图片_显示存储在mysql blob中的图像
  6. oracle存储过程转mysql存储过程修改方法
  7. vue ---- 组件
  8. Vue ---- 指令
  9. 自定义注解实现权限校验含义_厉害了!老大利用AOP实现自定义注解,半小时完成我三天工作量...
  10. MySQL按日期分组统计(按天统计,按月统计)