MIT6.5830 实验3

前置回顾

在实验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 herepageNumber  int                // 有多少页 page, 是 map的逻辑容量heapPageMap map[heapHash]*Page // 缓存的 pagemu          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    *PagepageNo  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.mzph.cn/news/667201.shtml

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

相关文章

MicroPython ESP32开发:通过寄存器直接访问外围设备

可以通过直接读写寄存器来控制 ESP32 的外设。这就需要阅读数据手册,了解要使用哪些寄存器以及要写入哪些值。下面的示例展示了如何打开和更改 MCPWM0 外设的预分频器。 from micropython import const from machine import mem32# Define the register addresses …

面向对象OOP

一、面向对象的定义 基于"类"和"对象"的"组件化"编程思想 二、面向对象的核心思想 封装 继承 多态 (1)封装 : 隐藏代码实现细节,提高简洁性 (2)继承 :代码的复用,通过定义父类,子类在父类基础上…

【STM32+HAL库+CubeMX】UART轮询收发、中断收发、DMA收发方法及空闲中断详解

(转载)原文链接:https://blog.csdn.net/qq_39344192/article/details/131470735 1. 什么是UART? UART是一种异步串行通信接口,常用于通过串口与外部设备进行通信。它通过发送和接收数据帧来实现数据传输,使…

Android开发中,Vue 3处理回退按键事件

vue3有一些变化,按照网上有些文章的方法,发现行不通,通过一段时间的打印、尝试后,发现以下方法可行。 第一步)首先定义一个处理回退事件的js函数,一定是vue.methods中的函数,否则找不到this&am…

postman 文档、导出json脚本 导出响应数据 response ,showdoc导入postman json脚本 导出为文档word或markdown

保存、补全尽可能多的数据、描述 保存响应数据 Response:(如果导出接口数据,会同步导出响应数据) 请求接口后,点击下方 Save as Example 可以保存响应数据到本地(会在左侧接口下新增一个e.g. 文件用来保…

使用_NT_SYMBOL_PATH在启动VS前设置PDB路径

一、背景 由于公司相关项目的开发管理方式,导致公司会存在多个分支的版本正在开发/测试中。 在这样的背景下,我的日常工作中有时会出现存在某个分支的项目软件的某个功能出现了问题需要我去排查解决,而我当前并不在该分支上开发。于是只能安装…

C++泛编程(3)

类模板基础 1.类模板的基本概念2.类模板的分文件编写3.类模板的嵌套 (未完待续...) 在往节内容中,我们详细介绍了函数模板,这节开始我们就来聊一聊类模板。C中,类的细节远比函数多,所以这个专题也会更复杂。…

Llama2大模型开源,大模型的Android时代来了?

就昨天凌晨,微软和Meta宣布Llama2大模型开源且进一步放开商用,一下朋友圈刷屏。要知道,开源界最强大的模型就是过去Meta开源的Llama,而现在Llama2更强大,又开放商用,更有微软大模型霸主企业撑腰(微软既投资大模型界的IOS——ChatGPT,又联合发布大模型的Android——Llam…

【2月比赛合集】28场可报名的数据挖掘大奖赛,任君挑选!

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…)比赛。本账号会推送最新的比赛消息,欢迎关注! 以下信息仅供参考,以比赛官网为准 目录 Kaggle(2场比赛)阿里天池(…

数据库学习笔记2024/2/4

随笔 1. 为什么学? 认识数据,熟悉数据,掌握数据。 进企业必备技能。 2. 怎么学? 1、MySQL数据库就是存储和管理数据的一个大型软件,这个软件有一个专门的语言叫SQL,主要学的是SQL语言,但想要达到企业用人标准,就还得学会熟练使用MySQL这个软件。 2、学习分三阶段: 一. …

salesforce的公式字段如何显示多个空格

公式字段会自动剔除文本字段中的额外空格,以防止显示过多的空格。如果需要显示多个空格,可以使用非换行空格符 (这在某些上下文中可以被视为一个单独的空格)。例如: " "

倒计时64天

B-小红的因子数_牛客周赛 Round 31 (nowcoder.com) 超时了&#xff08;108.33/150&#xff09; #include <bits/stdc.h> using namespace std; const int N 1e5 5; const int inf 0x3f3f3f3f; #define int long long void solve() {int x;cin>>x;if(x1){cout&…

微信小程序课设(基于云开发)

微信小程序通过Laf云平台接入ChatGPT实现聊天&#xff0c;回答方式采用流式回答。 以下是图片展示其页面 回答次数通过卡密兑换 以下是对话页面的代码 <!--pages/content/content.wxml--><view class"container"><view class"div" hidde…

软件测试学习笔记-使用jmeter进行接口测试

使用jmeter之前首先需要安装jdk&#xff0c;配置其环境变量&#xff0c;然后再安装jmeter 软件测试学习笔记-使用jmeter进行接口测试 1. 断言1. 响应断言2. JSON断言 2. 参数化1. 用户定义的变量2. CSV参数化 3. 接口关联4. 连接数据库 1. 断言 对某个接口其中一些信息进行判断…

DP第一天:力扣● 理论基础 ● 509. 斐波那契数 ● 70. 爬楼梯 ● 746. 使用最小花费爬楼梯

● 理论基础 DP大约五种问题&#xff1a; 动规基础&#xff08;斐波那契数列、爬楼梯&#xff09;&#xff1b;背包问题&#xff1b;股票问题&#xff1b;打家劫舍&#xff1b;子序列问题。 要搞清楚&#xff1a; DP数组及其下标的含义&#xff1b;DP数组如何初始化&#x…

DiskGenius v4.30专业版下载

DiskGenius是一款专业级的数据恢复软件&#xff0c;算法精湛、功能强大&#xff0c;用户群体广泛&#xff1b;支持各种情况下的文件恢复和分区恢复&#xff0c;恢复效果好&#xff1b;文件预览、扇区编辑、加密分区恢复、Ext4分区恢复、RAID恢复等高级功能应有尽有&#xff0c;…

C++设计模式-简单工厂模式,工厂方法模式,抽象工厂模式

目录 简单工厂模式&#xff0c;工厂方法模式&#xff0c;抽象工厂模式 附&#xff1a; 简单工厂模式&#xff0c;工厂方法模式&#xff0c;抽象工厂模式 简单工厂模式&#xff1a;根据字符串参数返回对象。 工厂方法模式&#xff1a;创建一维对象&#xff0c;即一个工厂创建…

Vue3+TS+Vite+Pinia最全学习总结

VUE3介绍 vue2和vue3之间的区别 因为需要遍历data对象上所有属性&#xff0c;所以如果data对象属性结构嵌套很深&#xff0c;就会存在性能问题。因为需要遍历属性&#xff0c;所有需要提前知道对象上有哪些属性&#xff0c;才能将其转化为getter和setter,所以vue2中无法将data新…

嵌入式Linux:main函数的使用方法

和单片机开发一样&#xff0c;在Linux中&#xff0c;C语言程序的执行通常从main函数开始。main函数是程序的入口点&#xff0c;当程序启动时&#xff0c;操作系统会调用main函数来执行程序的主要逻辑。 不同于单片机开发&#xff0c;通常设置main函数没有参数&#xff0c;Linu…

一起畅玩!幻兽帕鲁服务器1分钟开服,不服来战!

玩转幻兽帕鲁服务器&#xff0c;阿里云推出新手0基础一键部署幻兽帕鲁服务器教程&#xff0c;傻瓜式一键部署&#xff0c;3分钟即可成功创建一台Palworld专属服务器&#xff0c;成本仅需26元&#xff0c;阿里云服务器网aliyunfuwuqi.com分享2024年新版基于阿里云搭建幻兽帕鲁服…