MIT6.5830 实验3

news2024/11/28 0:34:00

前置回顾

在实验2中,完成了增删查改、排序、分组、聚合、连接等基本操作,在已提供 sql 解析器的基础上,能够运行进本的 sql 语句。都是逻辑层的实现,没有涉及物理存储方面的内容。

实验目标

实现最简单的基于锁的transaction,事务是一组以原子方式执行的数据库操作(例如,插入、删除和读取),要么所有操作都完成,要么没有一个完成。

关系型数据库的事务 acid 特性:

  • Atomicity(原子性):一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。

  • Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。

  • Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。

  • Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

实现思路

Acid 支持

  1. 最重要的原子性用 golang 的锁实现,互斥锁。 lab3.readMe 中指出,建议锁的粒度是 Page, 而非表级。实际上页面粒度已经很大了,在 MySQL中,一些锁的粒度是到行级。

  2. 隔离性,因为采用 NO STEAL 策略,所以隔离级别是读提交

  3. 持久性,因为采用 FORCE 策略,所以有一定的持久性,但不是100%

  4. 因为保证上述三个特性,所以默认有一致性保障,但一些如外键,not null 等等约束没有实现

STEAL/FORCE

STEAL/FORCE 概念指的是数据库的缓冲池管理和事务提交策略, 本实验使用的是 NO STEAL & FORCE 策略。

关于 NO STEAL / STEAL:如果 Buffer Pool缓存区已满,应该逐出哪些页。

  • STEAL: 允许事务“偷取”未提交副本的缓冲区空间。也就是说,即使一些脏页还未提交,也可以将这些脏页从缓冲区中删除,刷到磁盘中。这是最常用的方法,因为它有效地利用了缓冲区空间,但会导致未提交的内容写入磁盘,需要使用WAL手段来保证一致性。

  • NO STEAL: 不允许事务“偷取”未提交副本的缓冲区空间。换句话说,一个事务修改的页在该事务提交之前不能被换出。这意味着需要足够的缓冲区来处理每个事务。

关于 NO FORCE/FORCE:指的是事务提交时是否立刻刷盘。

  • FORCE: 在事务提交时,立即将所有修改的页写入磁盘。这意味着事务一旦提交,所有的修改都必须持久化,从而简化了在系统崩溃时的恢复过程。然而,这可能会导致大量的磁盘I/O操作。

  • NO FORCE: 不要求事务提交时立即将其修改的页写入磁盘。这意味着即使事务已经提交,其修改的页可能仍然保留在内存中,并在以后的某个时间点写回磁盘。这减少了磁盘I/O的数量,但在崩溃恢复时增加了复杂性。

大多数现代的数据库系统实际上使用的是 STEAL + NO FORCE 策略,因为它提供了在运行时和恢复时的效率之间的一个良好的折衷。这种策略依赖于数据库的事务日志(Transaction Log)来在系统崩溃之后恢复未提交的事务。

锁是实现原子性的关键手段。因为所有上层sql和事务等逻辑操作和底层存储操作之间的媒介都是通过 Buffer Pool,所以事务的实现在Buffer Pool中。按照实验提示锁的粒度是页面级的,所以我们可以在 pool 中采用一种数据结构维护某些事务涉及某些页面。

根据实验提示,事务需要的锁分为共享锁和排它锁,规则如下:

  • 在事务可以读取对象之前,它必须拥有共享锁。

  • 在事务可以写入对象之前,它必须拥有独占锁。

  • 多个事务可以在一个对象上拥有共享锁。

  • 只有一个事务可以对某个对象拥有独占锁。

  • 如果事务 t 是在对象 o上持有共享锁的唯一事务, 则 t 可以 将 其对 o 的锁升级 为排他锁。

额外要注意的是死锁,关于死锁的概念涉及:

  1. 死锁形成的四个必要条件[可搜]

  2. 处理死锁的方法一般有死锁预防、检测和恢复。在实验中采取的是预防,即事务在加锁前先算一下如果加锁会不会导致死锁,如果会就当前回滚事务。

Exercise 1

根据实验手册,首先实现 Buffer Pool 的 GetPage 方法。根据上述实现思路,首先要搞清楚 Buffer Pool 和 事务、Page之间的关联关系。

基于上面的关系,采用 tidLockMap 和 waitTidLockMap 来维护对应的关系。

type BufferPool struct {
    // TODO: some code goes here
    pageNumber  int                // 有多少页 page, 是 map的逻辑容量
    heapPageMap map[heapHash]*Page // 缓存的 page
    mu          sync.Mutex         // 保证 map 不会并发的锁,也是 buffer pool 的整体锁

    // You will want to allocate data structures that keep track of the shared and exclusive locks each transaction is currently holding
    // 针对每一个事务,要维护他们所持有的锁list[对应页面list]
    // 实验中提到,锁的粒度是 page, 一个 page 上可以有多个事务读锁,一个事务会锁住多个页面,value 如果是 list 的话会有性能问题
    tidLockMap map[TransactionID]map[heapHash]pageLock

    // 为了死锁的检测和预防,需要维护一个锁等待队列, 某个tid 在等待哪些其他 tid . value 如果是 list 的话会有性能问题
    waitTidLockMap map[TransactionID]map[TransactionID]struct{}
}

// 实验中提到,锁的粒度是 page, 一个 page 上可以有多个读锁,一个锁也可以锁住多个页
type pageLock struct {
    page    *Page
    pageNo  int      // 从 page 里面也能拿到,但需要强转类型
    perm    RWPerm   // 读锁/写锁
    pageKey heapHash // Abort事务的时候,需要将页面从Buffer Pool中删除,需要获取页面Key
}

基本上构思出 Buffer Pool 的数据结构,对于代码的逻辑就很简单了。 GetPage 函数的基本框架不变,只是在之前的逻辑中插入处理事务相关即可【左图】。如何处理事务【右图】,首先是判断一下当前页面上是否有其他事务加的锁,如果有进一步判断是否可能会形成死锁。在每次 for 循环之间需要 sleep 一下并且释放buffer pool的整体锁,减少和其他事务共同形成死锁的可能。

判断当前页面上是否有其他事务加的锁逻辑【左图】,死锁的检测逻辑【右图】。

Exercise 2

实现 BeginTransaction()CommitTransaction()AbortTransaction()方法。

Exercise 3

在整个 GoDB 中添加同步控制块,对于本实现方法来说,就是在 heap_file.go 中的 insert 逻辑中添加互斥锁。另外,实验1中的实现已经不满足事务的性能要求,所以需要重写,代码和注释如下。

Exercise 4

不能驱逐脏页。在 Exercise 1 中的 buffer pool 的 GetPage 函数中已经实现。

Exercise 5

死锁的预防, 在 Exercise 1 中的 buffer pool 的 deadLockPrevent 函数已经实现。可以把 waitTidLockMap 看成是一个有向图的邻接表,每次需要使用 dfs 算法检测一下是否可能形成环。

总结

在完成所有Exercise代码之后, transaction_test.go deadlock_test.go 以及 locking_test.go 都能顺利通过。实际上事务的实现非常复杂,但很多小型精炼的数据库都对事务进行了简化,例如本次实验的事务的粒度只到 Page ,实际上会产生很多问题,有时候满足不可重读的隔离性有时候不满足。 例如同样是纯 go 实现的 kv 文件数据库 blot db 不支持并发写事务,就不存在各种锁、mvcc 等复杂点。

联系方式

francis_l@qq.com

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1425651.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RabbitMQ之死信交换机

前言 消息队列是分布式系统中常用的组件,用于异步通信、解耦和提高系统可靠性。然而,在实际应用中,难免会遇到一些异常情况,例如消息处理失败、超时等。为了更好地处理这些异常情况,死信交换机(Dead Lette…

搭建高效企业培训平台:教育系统源码开发详解

为了更好地满足企业培训的需求,许多组织纷纷转向数字化教育,搭建高效的企业培训平台成为当务之急。本篇文章,小编将为您讲解教育系统源码的开发细节,为搭建一个功能强大、灵活高效的企业培训平台提供详尽的指南。 一、教育系统的…

“2024成都国际自动驾驶技术展览会”展示前沿技术与创新融合

近年来,新一轮科技革命和产业革命正向纵深发展,以互联网为代表的新一代信息技术与汽车产业的加速融合推动了汽车产品形态和分布的深刻变革,汽车已开始向大型移动智能终端的方向演变。汽车、信息、互联网等企业、研究院所、高校及各国政府纷纷…

单细胞scRNA-seq测序基础知识笔记

单细胞scRNA-seq测序基础知识笔记 scRNA-seq技术scRNA-seq 分析流程数据预处理聚类标准化数据筛选有用的数据数据降维聚类 Clustering 注释细胞类型 scRNA数据分析结尾 该笔记来源于 B站up 江湾青年 scRNA-seq技术 首先是如何测序,上图瓶中有很多细胞,…

echarts 饼图循环高亮展示

echarts 饼图循环高亮展示 this.categorychart.setOption(option);let currentIndex 0; // 当前高亮图形在饼图数据中的下标selectPie()if (this.changePieInterval)clearInterval(this.changePieInterval);this.changePieInterval setInterval(selectPie, 5000); // 设置自动…

实现vue3响应式系统核心-shallowReactive

简介 今天来实现一下 shallowReactive 这个 API。 reactive函数是一个深响应,当你取出的值为对象类型,需要再次调用 reactive进行响应式处理。很明显我们目前的代码是一个浅响应,即 只代理了对象的第一层,也就是 shallowReactiv…

以 AI 升级自我 | Kyligence 荣获多个奖项及榜单认可

回顾 2023 年的企业开年信,Kyligence 联合创始人兼 CEO 韩卿借用了历史上一句经典口号“时间就是金钱,效率就是生命”,鼓励团队顺势而为,抓住时代的机会,快速发展,快速成长。 Kyligence 一直都在践行“Ret…

【系统设计】12306架构设计难点(下)

欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术的推送! 在我后台回复 「资料」 可领取编程高频电子书! 在我后台回复「面试」可领取硬核面试笔记! 文章导读地址…

Linux第38步_编译“正点原子移植好的uboot”

uboot的全称是Universal Boot Loader,uboot是一个遵循GPL协议的开源软件,uboot是一个裸机代码,可以看作是一个裸机综合例程。现在的 uboot 已经支持液晶屏、网络、USB等高级功能。 uboot官方的uboot源码是给所有的半导体厂商准备的。ST公司会…

c++ 字符串切分split

c 字符串切分split 的举例实现 一共给出了四种方式 1、 strtok 2、 stringstream 3、 字符串查找 4、 基于封装的方式&#xff0c;提供了 c11 foreach 接口 代码 vector<string> split(string s) {vector<string> res;const char *p strtok((char *) s.c_str(),…

【LVGL源码移植】

LVGL源码移植 ■ LVGL源码移植一&#xff1a;下载LVGL源码二&#xff1a;修改LVGL文件夹1: 将这5个文件&#xff0c;复制到一个新的文件夹2: 简化文件&#xff0c;减少内存消耗&#xff08;去除不必要的文件&#xff09;3: 为了规范化&#xff0c;我们将下列文件进行重命名 三&…

强化学习 - Monte Carlo Tree Search (MCTS)

什么是机器学习 强化学习中的Monte Carlo Tree Search (MCTS) 是一种用于决策制定和搜索的算法&#xff0c;特别在不确定环境下表现出色。 1. 强化学习背景 在强化学习中&#xff0c;一个智能体通过与环境的交互学习&#xff0c;以便在某个任务上获得最大的奖励。MCTS是一种…

Kotlin 协程:深入理解 ‘lifecycleScope‘

Kotlin 协程&#xff1a;深入理解 ‘lifecycleScope’ Kotlin 协程是一种强大的异步编程工具&#xff0c;它提供了一种简洁、易读的方式来处理并发和异步操作。在 Kotlin 协程库中&#xff0c;lifecycleScope 是一个关键的概念&#xff0c;它允许我们将协程的生命周期绑定到 An…

LeetCode 828. 统计子串中的唯一字符

一开始想的是两次前缀和&#xff0c;发现自己蠢了 看了灵神的题解&#xff0c;类似于DP的思想 我们维护以每个字符串结尾的子字符串对答案的贡献&#xff0c;s[i]的贡献是多少&#xff1f;首先我们知道他需要自己单独一个串或者接在以s[i-1]结尾的那些字符串的后面&#xff0c…

从法律风险的角度看待项目验收前自测的必要性

大家好&#xff0c;我是不会魔法的兔子&#xff0c; 一枚从事企业合同纠纷预防与解决的执业律师&#xff0c;从法律的角度分享关于项目管理中的问题及预防&#xff0c;让项目管理者能够提早发现与解决项目执行过程中的风险&#xff0c;同时欢迎大家一起交流&#xff0c;微信搜…

做外贸发货时发现货物有问题要怎么办

一个客户向一个伙伴订购了一批衣服&#xff0c;在准备装货的时候&#xff0c;小伙伴到工厂检查货物&#xff0c;发现衣服非常的潮湿&#xff0c;于是小伙伴担心货物万一经过长期的海运&#xff0c;到达客户那边发霉了怎么办呢&#xff1f; 但是工厂这边已经打好包装&#xff0c…

MySQL备份和恢复(二)mysqldump

注意&#xff1a;mysqldump是完全备份 一、mysqldump备份命令 1、 备份数据库 含创建库语句 &#xff08;1&#xff09;备份指定数据库 完全备份一个或多个完整的库&#xff0c; mysqldump -uroot -p[密码] --databases 库名1 [库名2].. >/备份路径/备份文件名.sql#导出…

CS144--Chapter0--wsl2+docker环境搭建

我的笔记本配置 荣耀magicbook16&#xff0c;容量是500G&#xff0c;芯片是R7-5800 由于笔记本容量较小&#xff0c;因此考虑这个方案&#xff0c;对于台式机用户&#xff0c;建议可以直接用虚拟机或者双系统。 前言 斯坦福官网给出的方法是用他们的镜像&#xff08;基于Ubu…

【Vue】2-8、Axios 网络请求

cdn&#xff1a;<script src"https://unpkg.com/axios/dist/axios.min.js"></script> 注&#xff1a;使用 CDN 链接就可以不需要去下载对应的 js 文件到本地&#xff0c;只需要联网即可使用&#xff0c;可以减少项目的体积 <!DOCTYPE html> <…

minio文件跨域问题

问题&#xff1a; 最近前端获取音频的波形报了个错&#xff1a; Access to audio at http://xxx/03.wav from origin http://zzz has been blocked by CORS policy: The Access-Control-Allow-Origin header contains multiple values zzz, *, but only one is allowed. 很奇怪…