CMU15-445 Project.4总结

news2024/9/23 3:25:08

在线测试
在这里插入图片描述

Project #4 - Concurrency Control

以下是Project #4的网址,2022FALL的Project #4是实现并发控制,可以分为以下三个任务:

  1. 我们首先需要实现一个锁管理器,能够支持 READ_UNCOMMITED、READ_COMMITTED、REPEATABLE_READ 三个隔离级别,以及相应的上锁解锁;
  2. 其次我们需要实现死锁检测,并能够中止最年轻的事务;
  3. 最后我们需要对 Project 3 中实现的 SeqScanExecutor 、InsertExecutor 、DeleteExecutor 三个算子进行改进,使其能够支持相应的上锁解锁以实现不同的隔离级别。

我们首先需要明确, Bustub 中的锁管理器通过 2PL 实现并发控制,其中的三种隔离级别分别为:

  1. REPEATABLE_READ(可重复读):这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。
  2. READ_COMMITTED (读已提交):这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。
  3. READ_UNCOMMITTED(读未提交):在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。

他们的性能依次更好但是一致性依次更差,分别可能会出现以下三个问题:

  1. 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
  2. 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
  3. 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
    在这里插入图片描述

此外,Bustub 中的锁管理器支持 table 和 row 两种粒度,S、X、IS、IX、SIX 五种锁类型:

  1. S锁(共享锁):加了S锁的记录,允许其他事务再加S锁,不允许其他事务再加X锁;
  2. X锁(排他锁):加了X锁的记录,不允许其他事务再加S锁或者X锁;
  3. IS 锁(读意向锁):当事务要在记录上加上读锁时,要首先在表上加上读意向锁;
  4. IX 锁(写意向锁):当事务要在记录上加上写锁时,要首先在表上加上写意向锁;
  5. SIX 锁(共享写意向锁):在已经获得表或者页级别的S锁之后又分配IX给它。

在这里插入图片描述

任务 #1 - 锁管理器

在锁管理器中包含了如下结构:

  • table_lock_map_:记录 table 和与锁请求队列;
  • row_lock_map_:记录 row 和与锁请求队列;
  • request_queue_:实际存放锁请求的队列;
  • cv_ 和 latch_:条件变量和锁,用于实现多线程的协同等待;
  • upgrading_:正在此资源上尝试锁升级的事务 id;
  • txn_id_:发起此请求的事务 id ;
  • lock_mode_:请求锁的类型;
  • oid_:在 table 粒度锁请求中,代表 table id。在 row 粒度锁请求中,表示 row 属于的 table 的 id;
  • rid_:仅在 row 粒度锁请求中有效。指 row 对应的 rid;
  • granted_:是否已经对此请求授予锁。

在锁管理器中,我们需要对事务发送的锁请求进行处理,主要包括了上锁和解锁请求。

Lock

  • LockTable()LockRow()都是阻塞方法;它们应该等待,直到锁被授予,然后返回。如果事务在此期间被中止,则不授予锁,并返回false。
  • LockManager 应该为每个资源维护一个队列;应该以先进先出的方式向事务授予锁。只要FIFO被遵守,如果有多个兼容的锁请求,所有的锁应该同时被授予。
  • 表锁应该支持所有的锁模式;行锁不应该支持意图锁。试图这样做应该把交易状态设置 ABORTED并抛出一个 TransactionAbortException (ATTEMPTED_INTENTION_LOCK_ON_ROW)。
  • 根据隔离级别,当事务在不允许的情况下发出锁请求时,应抛出相应的异常 TransactionAbortException(LOCK_SHARED_ON_READ_UNCOMMITTED)和TransactionAbortException(LOCK_ON_SHRINKING)。其中:
    • repeatable_read:事务需要占用所有的锁;在GROWING状态下允许有所有的锁;在SHRINKING状态下不允许有锁。
    • read_committed:该事务需要取得所有的锁;在GROWING状态下允许有所有的锁; 在SHRINKING状态下只允许使用IS, S锁。
    • read_uncommitted:事务被要求只采取IX, X锁;在GROWING状态下允许使用X, IX锁; S, IS, SIX锁是绝对不允许的。
  • 在锁定行的时候,Lock()应该确保事务在该行所属的表上有一个适当的锁。属于哪个表。例如,如果试图对某行进行独占锁,事务必须持有X, IX, 或者SIX。如果表上不存在这样的锁,Lock()应该把TransactionState 为 ABORTED,并抛出一个TransactionAbortException(TABLE_LOCK_NOT_PRESENT)。
  • 在一个已经被锁定的资源上调用Lock()应该有以下行为:如果请求的锁模式与目前持有的锁的模式相同,Lock()应该返回true,因为它已经有了锁; 如果请求的锁模式不同,Lock()应该升级事务所持有的锁。被升级的锁请求应该优先于同一资源上的其他等待的锁请求。
    • 在升级时,只允许有以下的转换。任何其他的升级被认为是不兼容的,这样的尝试应该把交易状态设置为 ABORTED 并抛出一个TransactionAbortException(INCOMPATIBLE_UPGRADE)。
    • IS -> [S, X, IX, SIX].
    • S-> [X, SIX]
    • IX-> [X, SIX]
    • SIX-> [X]
  • 此外,在一个给定的资源上,只允许一个事务升级其锁。在同一资源上的多个并发的锁升级应该把交易状态设置为 ABORTED并抛出一个TransactionAbortException (UPGRADE_CONFLICT)。
  • 如果一个锁被授予一个事务,锁管理器应该更新它的适当的锁集.

基于以上需求,我们按照如下的流程进行上锁:

  1. 检查事务的隔离等级与当前上锁类型之间的对应关系,若不满足需要抛出相应异常;
  2. 给储存 table 和锁请求队列的哈希表进行上锁,获得当前表对应的锁请求队列,而后对队列上锁并对哈希表解锁。若队列不存在则需要创建默认队列。
  3. 遍历锁请求队列判断是否为锁升级请求:
  • 判断此前授予锁类型是否与当前请求锁类型相同。
  • 判断当前资源上是否有另一个事务正在尝试升级。
  • 判断升级锁的类型和之前锁是否兼容。
  1. 在确认当前锁请求合法之后,我们释放当前已经持有的锁,并把新的锁请求作为一个新的请求加入队列中尚未执行的事务最前方,在 upgrading_中标记当前事务的 id 。
  2. 利用条件变量和互斥锁实现线程同步。我们首先持有锁判断能否获得资源:若无法获得则调用条件变量的 wait 函数,自动释放 latch 并挂起线程;当其他线程完成使用资源之后会发出 notify_all 函数唤醒所有因此资源而阻塞的线程,被唤醒的线程在 wait 函数尝试获取 latch ,成功获取之后退出当前 wait 函数并尝试获取资源:若不能则继续重复 wait 过程;若能则退出循环。
  3. 基于以上机制,我们在GrantLock中判断能否满足当前锁请求,我们需要首先判断有的已经 granted 的请求与当前的锁请求是否兼容,若可以则返回 true 跳出循环。
  4. 最终我们将 granted_ 置为 true,并根据情况将 upgrading_ 重置为 INVALID_TXN_ID,并更新 Transaction 中持有的锁集合。
  5. 值得注意的是,当我们在对行进行上锁时,我们还需要判断所在表是否具有相应的意向锁。

Unlock

  • UnlockTable()和UnlockRow()都应该释放资源上的锁并返回。两者都应该确保事务当前对它试图解锁的资源持有锁。如果不是,LockManager应该将TransactionState设置为 ABORTED 并抛出TransactionAbortException (ATTEMPTED_UNLOCK_BUT_NO_LOCK_HELD).
  • 此外,只有在事务没有在表上持有锁的情况下,才允许解锁该表。该表的任何行。如果事务在该表的行上持有锁,那么解锁应该将事务状态设置为 为ABORTED,并抛出一个TransactionAbortException(TABLE_UNLOCKED_BEFORE_UNLOCKING_ROWS)。
  • 解锁一个资源也应该授予该资源的任何新锁请求。
    • repeatable_read: 解锁S/X锁应该将事务状态设置为SHRINKING。
    • read_committed:解锁X锁应该将事务状态设置为SHRINKING。解除S锁不会影响事务状态。
    • read_uncommitted:解除X锁应该将事务状态设置为SHRINKING。S锁在READ_UNCOMMITTED下是不允许的。在这个隔离级别下,解锁S锁时的行为是未定义的。
  • 在一个资源被解锁后,锁管理器应该适当地更新事务的锁集。

基于以上需求,我们按照如下的流程进行上锁:

  1. 给储存 table 和锁请求队列的哈希表进行上锁,获得当前表对应的锁请求队列,而后对队列上锁并对哈希表解锁。若队列不存在则需要创建默认队列。
  2. 遍历请求队列,找到 unlock 对应的 granted 请求,根据当前的隔离级别和锁类型修改状态并删除锁请求。
  3. 更新 Transaction 中持有的锁集合,并调用notify_all唤醒其他被阻塞的线程。

任务 #2 - 死锁检测

为了避免多个线程之间的循环等待造成死锁,我们需要利用 wait for 图来判断当前的所有进程之间是否存在死锁的情况。值得注意的是,我们并不需要移植维护 wait for 图,只需要在死锁检测线程被唤醒时,根据当前请求队列构建 wait for 图,而后便可以丢弃当前构建的 wait for 图。

在 wait for 图中,我们利用 t 1 − > t 2 t1->t2 t1>t2 表示 t1 事务正在等待 t2 事务释放资源。若 wait for 图中存在环则说明存在死锁情况。我们可以利用 DFS 来进行环检测,其中利用哈希表来储存节点和该节点的邻居节点。我们利用 txn_set_ 表示所有已获得运行许可的线程节点集合,利用 safe_set_ 表示已经遍历完所有邻居节点的节点,利用 active_set_ 表示尚有邻居节点未遍历完的节点。若我们在遍历邻居节点时遍历到了 active_set_ 中的节点说明图中存在环,若为遍历到则递归调用函数并判断其邻居节点出发是否会出现环。若仍没有则可以将当前节点加入 safe_set_ 中。我们按照 tid 从大到小的顺序来遍历每一个节点,这样就就能够确保当我们需要删除造成死锁的节点时,我们优先删除的是 tid 最大的事务,也就是最年轻的事务。

在找到需要删除的事务之后,我们将其状态设为 Aborted。并且在请求队列中移除此事务,释放其持有的锁,终止其正在阻塞的请求,并调用 notify_all通知正在阻塞的相关事务。此外,还需移除 wait for 图中与此事务有关的边。因此当事务被唤醒时,需要确认其是否处于 Aborted 状态,若处于则释放所持资源并返回。

任务 #3 - 并发查询执行

SeqScan

如果隔离级别是 READ_UNCOMMITTED 则无需加锁。加锁失败则抛出 ExecutionException 异常。在 READ_COMMITTED 下,在 Next() 函数中,若无数据则提前释放之前持有的锁。在 REPEATABLE_READ 下,在 Commit 或 Abort 时统一释放。

Insert & Delete

Init() 函数中,为表加上 IX 锁,再为行加 X 锁。同样,若获取失败则抛 ExecutionException 异常。另外,这里的获取失败不仅是结果返回 false,还有可能是抛出了 TransactionAbort 异常,例如 UPGRADE_CONFLICT,需要用 try catch 捕获。锁在 Commit 或 Abort 时统一释放。

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

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

相关文章

layui表格合并

先看一下最终合并之后的效果,能对单选、复选框进行按照某一列的合并 最开始的解决方案来自于这篇博客介绍的方法:https://blog.csdn.net/guishifoxin/article/details/81480136 但是还是存在没能解决的问题。 在完善之后达到的效果: 一&…

移动硬盘格式化?想要恢复硬盘那就看这里!

案例:移动硬盘无法打开,提示格式化? “怎么办啊!!!今天下午给同学重装系统,插上自己的移动硬盘,却发现读不出来,提示需要格式化!里面有很多东西,…

第十四章 opengl之高级OpenGL(深度测试)

OpenGL深度测试深度测试函数深度值精度深度缓冲的可视化深度冲突防止深度冲突深度测试 前面我们渲染一个3D图片中运用了深度缓冲:防止被阻挡的面渲染到其他面的前面。 深度缓冲就像颜色缓冲(Color Buffer)(储存所有的片段颜色:视觉输出&…

JAVA开发(JAVA中的异常)

在java开发与代码运行过程中,我们经常会遇到需要处理异常的时候。有时候是在用编辑器写代码,点击保存的时候,编辑器就提示我们某块代码有异常,强制需要处理。有时候是我们启动,运行JAVA代码的时候的,日志里…

案例06-没有复用思想的接口和sql--mybatis,spring

目录一、背景二、思路&方案问题1优化问题2优化三、总结四、升华一、背景 写这篇文章的目的是通过对没有复用思想接口的代码例子优化告诉大家,没有复用思想的代码不要写,用这种思维方式和习惯来指导我们写代码。 项目中有两处没有复用思想代码&#…

R语言基础(三):运算

接前文 R语言基础(一):注释、变量 R语言基础(二):常用函数 4.运算 4.1 数学运算 R语言中支持加减乘除四则运算、乘方运算、求余数(取模)运算: 符号含义示例加法11 结果是2-减法2-1 结果是1*乘法4*5 结果是20/除法4/5 结果是0.8%/%整除(只要…

MySQL 事务隔离

MySQL 事务隔离事务隔离实现事务的启动ACID : 原子(Atomicity)、一致(Consistency)、隔离(Isolation)、永久(Durability) 多个事务可能出现问题 : 脏读 (dirty read) , 不可重复读 (non-repeatable read) , 幻读 (phantom read) 事务隔离级别 : 读未提交 (read uncommitted)…

一篇学习ES

文章目录ES简介1.什么是ElasticSearch2.ElasticSearch的使用案例3.ElasticSearch对比SolrElasticSearch环境搭建1. 下载ES压缩包2. 安装ES服务3. 启动ES服务3. 安装ES的图形化界面插件ES术语1.概述2.索引 index3.类型 type4.字段Field5.映射 mapping6.文档 document7. 接近实时…

制造业数字化转型要注重哪些方面?

近年来,制造业企业数字化转型的话题一直处于行业高热位置。中央经济工作会议作出“大力发展数字经济”的部署,工信部提出要深化产业数字化转型,建设一批全球领先的智能工厂、智慧供应链,并向中小企业场景化、标准化复制推广。 随…

监控体系划分

按采集类型划分 1.基于 Metrics 的监控 基于 Metrics 的监控,背后对应的是度量(指标监控)系统,监控机器在某段时间内的 CPU 使用率、系统负载; HTTP 请求访问量等。 1.Skywalking 开源地址 (既能做调用链监…

Spring-AOP简介案例

Spring-AOP简介&案例 1,AOP简介 Spring有两个核心的概念,一个是IOC/DI,一个是AOP。 对于AOP,我们前面提过一句话是:AOP是在不改原有代码的前提下对其进行增强。 1.1 什么是AOP? AOP(Aspect Oriented Programming)面向切面编程&…

java Spring5 xml配置文件方式实现声明式事务

在java Spring5通过声明式事务(注解方式)完成一个简单的事务操作中 我们通过注解方式完成了一个事务操作 那么 下面 我还是讲一下 基于xml实现声明式事务的操作 其实在开发过程中 大家肯定都喜欢用注解 因为他方便 这篇文章中的xml方式 大家做个了解就好 还是 我们的这张表 记…

ECharts数据可视化--常用图表类型

目录 一.柱状图 1.基本柱状图 1.1最简单的柱状图 ​编辑 1.2多系列柱状图 1.3柱状图的样式 (1)柱条样式 (2)柱条的宽度和高度 (3)柱条间距 (4)为柱条添加背景颜色 ​编辑 2.堆…

SpringBoot创建和使用

目录 什么是SpringBoot SpringBoot的优点 SpringBoot项目的创建 1、使用idea创建 2、项目目录介绍和运行 Spring Boot配置文件 1、配置文件 2、配置文件的格式 3、properties 3.1、properties基本语法 3.2、读取配置文件 3.3、缺点 4、yml 4.1、优点 4.2、yml基本…

虚拟机下Linux系统磁盘扩容

在VM虚拟机中,我们经常会选择默认磁盘大小20G,用着用着才发现20G不够用,服务启动不了,就很尴尬,让我们今天一起来学习下,如何在虚拟机给磁盘扩容。一:关闭虚拟机,添加硬盘背景&#…

mysql Docker容器的安装(centos版)以及修改docker默认端口、解决1251问题

文章目录一、Docker的安装以及在Docker下进行mysql的安装1、安装Docker2、上传安装包并进行安装并启动docker3、 配置Docker的镜像加速器,这里使用阿里云的镜像4、刷新守护进程,并重启docker,检验镜像是否配置成功5、搜索并下载mysql镜像6、导…

超分扩散模型 SR3 可以做图像去雨、去雾等恢复任务吗?

文章目录前言代码及原文链接主要的点如何进行图像恢复前言 关于扩散模型以及条件扩散模型的介绍,大家可以前往我的上一篇博客:扩散模型diffusion model用于图像恢复任务详细原理 (去雨,去雾等皆可),附实现代码。 SR3是利用扩散模…

优化Facebook广告ROI的数据驱动方法:从投放到运营

“投放广告并不是最终的目的,关键在于如何最大程度地利用数据驱动的方法来提高广告投放的回报率(ROI)”Facebook广告是现代数字营销中最为常见和重要的广告形式之一。但是,要让Facebook广告真正发挥作用,需要通过数据驱…

Allegro如何自动添加测试点操作指导

Allegro如何自动添加测试点操作指导 在做PCB设计的时候,在一些应用场合下需要给PCB上的网络添加测试点,如下图 测试点除了可以手动逐个添加之外,Allegro还支持自动添加测试点,具体操作如下 点击Manufacture点击Testprep

PlantUML画出如女神漂亮的流程图

一:环境准备 1,本地安装好vscode 2,vscode安装PlantUML插件 3,本地安装java环境,我本地用的是jdk-11.0.178,配置好环境变量 4,在vscode上新建一个文件以wsd结尾,输入以下两行&#x…