当前位置: 首页 > news >正文

【MyDB】4-VersionManager 之 3-死锁及超时检测

【MyDB】4-VersionManager 之 3-死锁及超时检测

  • 死锁及超时检测
    • 案例背景
    • LockTable
    • 锁请求与等待管理 `add`
      • vm调用add
      • putIntoList,isInList,removeFromList
    • 死锁检测 hasDeadLock方法
    • 资源释放与重分配
  • 参考资料

死锁及超时检测

本章涉及代码:top/xianghua/mydb/server/vm/LockTable.java

案例背景

学习如何使用 LockTable 进行锁管理、死锁检测与解决之前,先了解一下死锁是如何发生的。假设我们有三个事务 T1、T2 和 T3,它们分别要访问两个资源 R1 和 R2。事务的执行顺序如下:

  1. T1 锁定 R1:然后尝试锁定 R2。
  2. T2 锁定 R2:然后尝试锁定 R1。
  3. T3 尝试锁定 R1

这种情况下,T1 和 T2 之间会产生死锁,而 T3 将会被阻塞在等待 R1 上。

执行过程

  1. T1 锁定 R1
    • T1 请求锁定资源 R1。
    • 因为 R1 未被占用,所以 LockTable 将 R1 锁定给 T1。
    • T1 继续执行,准备请求锁定 R2。
  2. T2 锁定 R2
    • T2 请求锁定资源 R2。
    • 因为 R2 未被占用,所以 LockTable 将 R2 锁定给 T2。
    • T2 继续执行,准备请求锁定 R1。
  3. T1 请求锁定 R2
    • T1 请求锁定 R2。
    • 由于 R2 已被 T2 锁定,T1 被加入到 R2 的等待队列中。
  4. T2 请求锁定 R1
    • T2 请求锁定 R1。
    • 由于 R1 已被 T1 锁定,T2 被加入到 R1 的等待队列中。
    • 现在形成了 T1 → R2 → T2 → R1 → T1 的循环等待,导致死锁。
  5. T3 尝试锁定 R1
    • T3 请求锁定 R1。
    • 由于 R1 已被 T1 锁定且 T2 在等待 R1,T3 被加入到 R1 的等待队列中。

LockTable

上一章提到了2PL会阻塞事务,直至持有锁的线程释放锁。这样就可能出现死锁。因此,在MyDB中需要能检测死锁。

我们可以将事务之间的这种等待关系抽象成有向边,例如 T j T_j Tj在等待 T i T_i Ti。这样,无数的有向边就可以形成一个图(不一定是连通图)。检测死锁的话只需要查看图中是否有环。

就MyDB而言,使用了一个LockTabel对象,通过在内存中维护这张图,而记录事务的等待关系。

注意,x2u和wait是列表,分别记录xid已经获得的资源的uid列表,和正在等待某个uid的xid列表。

public class LockTable {private Map<Long, List<Long>> x2u;  // 某个XID已经获得的资源的UID列表private Map<Long, Long> u2x;        // UID被某个XID持有private Map<Long, List<Long>> wait; // 正在等待UID的XID列表private Map<Long, Lock> waitLock;   // 正在等待资源的XID的锁private Map<Long, Long> waitU;      // XID正在等待的UIDprivate Lock lock;...
}

锁请求与等待管理 add

当一个事务请求获取某个资源时,LockTable 首先会检查该资源是否已被其他事务持有。如果没有被持有,资源将直接分配给请求的事务。如果资源已被占用,事务将进入等待状态,并存储在相应的等待队列中。

  1. 检查当前事务xid是否已经获得了数据uid(isInList(x2u, xid, uid)

  2. 判断uid是否被其他xid持有,u2x.containsKey(uid),假如未被其他xid持有,则直接获得

    u2x.put(uid, xid); // Map<Long, Long> u2x; UID被某个XID持有

    putIntoList(x2u, xid, uid); //Map<Long, List> x2u 某个XID已经获得的资源的UID列表

  3. 被持有则等待,向依赖等待图中添加当前等待关系,即waitU.put(xid, uid);,之后进行死锁检测hasDeadLock。如果检测到死锁,就撤销这条边,不允许添加,并撤销该事务。

    // 不需要等待则返回null,否则返回锁对象// 会造成死锁则抛出异常public Lock add(long xid, long uid) throws Exception {// 1. 加锁lock.lock();try {// 2. xid是否已经获得了uid,是则说明不需要等待,返回nullif(isInList(x2u, xid, uid)) {return null;}// 3. uid是否被其他xid持有if(!u2x.containsKey(uid)) {// 3.1 uid未被其他xid持有,则直接获得u2x.put(uid, xid);putIntoList(x2u, xid, uid);return null;}// 3.2 uid被其他xid持有,则等待waitU.put(xid, uid);//putIntoList(wait, xid, uid);putIntoList(wait, uid, xid); // 这里是为了方便死锁检测,因为死锁检测是从等待队列中选择一个xid来占用uidif(hasDeadLock()) {waitU.remove(xid);removeFromList(wait, uid, xid);throw Error.DeadlockException;}Lock l = new ReentrantLock();l.lock();waitLock.put(xid, l);return l;} finally {lock.unlock();}}

vm调用add

在vm中,当某个事务xid需要资源uid时,就会调用add,可以看到,add方法需要等待的时候,会返回一个上了锁的Lock对象。调用方在获取到该对象时,需要尝试获取该对象的锁,由此实现阻塞线程的目的,例如:

Lock l = lt.add(xid, uid);
if(l != null) {l.lock();   // 阻塞在这一步l.unlock();
}

putIntoList,isInList,removeFromList

由于之前说过,我们再复习一下LockTable中的x2u和wait是long->List ,

private Map<Long, List> x2u; // 某个XID已经获得的资源的UID列表

private Map<Long, List> wait; // 正在等待UID的XID列表

那当我们需要知道事务是否已经获得资源uid时,由于x2u.get(xid)得到的时一个资源list,因此,需要使用inInList来判断,uid是否在xid已经获得的资源list中。

同理,当需要向x2u中添加某个xid已经获得的资源uid,也不能直接添加,而是要先获得x2u已经获得的资源的uid list,之后再向其中添加,就用到了putIntoList方法。

public class LockTable {private Map<Long, List<Long>> x2u;  // 某个XID已经获得的资源的UID列表private Map<Long, Long> u2x;        // UID被某个XID持有private Map<Long, List<Long>> wait; // 正在等待UID的XID列表private Map<Long, Lock> waitLock;   // 正在等待资源的XID的锁private Map<Long, Long> waitU;      // XID正在等待的UIDprivate Lock lock;...
}

putIntoList

    /*** 将uid1添加到uid0对应的列表中* @param listMap* @param id0* @param id1*/private void putIntoList(Map<Long, List<Long>> listMap, long id0, long id1) {// 如果id0对应的列表不存在,则创建一个新的列表if(!listMap.containsKey(id0)) {listMap.put(id0, new ArrayList<>());}// 将id1添加到id0对应的列表中listMap.get(id0).add(0, id1);}

isInList(listMap,id0,id1)id1是否在id0对应的列表中

/*** 判断id1是否在id0对应的列表中* @param listMap* @param id0* @param id1* @return*/private boolean isInList(Map<Long, List<Long>> listMap, long id0, long id1) {List<Long> l = listMap.get(id0);if(l == null) return false;Iterator<Long> i = l.iterator();while(i.hasNext()) {long e = i.next();if(e == id1) {return true;}}return false;}

removeFromList从uid0对应的列表中删除uid1

    /*** 从uid0对应的列表中删除uid1* @param listMap* @param uid0* @param uid1*/private void removeFromList(Map<Long, List<Long>> listMap, long uid0, long uid1) {List<Long> l = listMap.get(uid0);if(l == null) return;Iterator<Long> i = l.iterator();while(i.hasNext()) {long e = i.next();if(e == uid1) {i.remove();break;}}if(l.size() == 0) {listMap.remove(uid0);}}

死锁检测 hasDeadLock方法

为了避免死锁,LockTable 实现了基于深度优先搜索(DFS)的死锁检测机制。通过遍历事务依赖图,系统可以检测到是否存在循环依赖,从而识别死锁。

需要注意这个图不一定是连通图。思路就是为每个节点设置一个访问戳xidStamp,都初始化为 -1,随后遍历所有节点,以每个非 -1 的节点作为根进行深搜,并将深搜该连通图中遇到的所有节点都设置为同一个数字,不同的连通图数字不同。这样,如果在遍历某个图时,遇到了之前遍历过的节点,说明出现了环。

在这里插入图片描述

    private Map<Long, Integer> xidStamp; // XID对应的stampprivate int stamp; // 当前stampprivate boolean hasDeadLock() {xidStamp = new HashMap<>();stamp = 1;for(long xid : x2u.keySet()) {Integer s = xidStamp.get(xid);if(s != null && s > 0) {continue;}stamp ++;if(dfs(xid)) {return true;}}return false;}private boolean dfs(long xid) {Integer stp = xidStamp.get(xid);if(stp != null && stp == stamp) {return true;}if(stp != null && stp < stamp) {return false;}xidStamp.put(xid, stamp);Long uid = waitU.get(xid); // 得到事务xid等待的资源uidif(uid == null) return false; // 没有等待的资源Long x = u2x.get(uid); //找到占用资源uid的事务xassert x != null;return dfs(x); // 继续搜索事务x}

可以在LockTabelTest中运行testLockTable以检测死锁的发生

    public void testLockTable() {LockTable lt = new LockTable();try {lt.add(1, 1);} catch (Exception e) {Panic.panic(e);}try {lt.add(2, 2);} catch (Exception e) {Panic.panic(e);}try {lt.add(2, 1);} catch (Exception e) {Panic.panic(e);}try {lt.add(1, 2);} catch (Exception e) {Panic.panic(e);}assertThrows(RuntimeException.class, ()->lt.add(1, 2));}

资源释放与重分配

在一个事务 commit 或者 abort 时,就可以释放所有它持有的锁,并将自身从等待图中删除。然后将资源锁重新分配给等待队列中的其他事务。

while 循环释放掉了这个线程所有持有的资源的锁,这些资源可以被等待的线程所获取:

    public void remove(long xid) {lock.lock();try {// 1. 释放xid所占用的资源列表uidsList<Long> uids = x2u.get(xid);if(uids != null) {while(uids.size() > 0) {Long uid = uids.remove(0);selectNewXID(uid); //   从等待队列中选择一个xid来占用uid}}// 2. 在waitU(正在等待资源的xid),x2u(某个XID已经获得的资源的UID列表),waitLock中删除xid:waitU.remove(xid);x2u.remove(xid);waitLock.remove(xid);} finally {lock.unlock();}}

selectNewXID

从 List 开头开始尝试解锁,还是个公平锁。解锁时,将该 Lock 对象 unlock 即可,这样业务线程就获取到了锁,就可以继续执行了。

    // 从等待队列中选择一个xid来占用uidprivate void selectNewXID(long uid) {// u2x:uid被某个xid持有// 1. 从u2x中释放uid,标记uid不再被xid占用u2x.remove(uid);// 2. 获取该uid的等待队列xidsList<Long> xids = wait.get(uid); // wait:uid被哪些xid等待if(xids == null) return;assert xids.size() > 0;// 3. 从等待队列中选择一个xid来占用uidwhile(xids.size() > 0) {long xid = xids.remove(0);if(!waitLock.containsKey(xid)) {continue;} else {u2x.put(uid, xid);Lock lo = waitLock.remove(xid);waitU.remove(xid);lo.unlock();break;}}if(xids.size() == 0) wait.remove(uid);}

参考资料

死锁及超时检测 | EasyDB (blockcloth.cn)

MYDB 7. 死锁检测与 VM 的实现 | 信也のブログ (shinya.click)

相关文章:

【MyDB】4-VersionManager 之 3-死锁及超时检测

【MyDB】4-VersionManager 之 3-死锁及超时检测 死锁及超时检测案例背景LockTable锁请求与等待管理 addvm调用addputIntoList&#xff0c;isInList&#xff0c;removeFromList 死锁检测 hasDeadLock方法资源释放与重分配 参考资料 死锁及超时检测 本章涉及代码&#xff1a;top/…...

【Linux】使用管道实现一个简易版本的进程池

文章目录 使用管道实现一个简易版本的进程池流程图代码makefileTask.hppProcessPool.cc 程序流程&#xff1a; 使用管道实现一个简易版本的进程池 流程图 代码 makefile ProcessPool:ProcessPool.ccg -o $ $^ -g -stdc11 .PHONY:clean clean:rm -f ProcessPoolTask.hpp #pr…...

【OpenGL】OpenGL游戏案例(二)

文章目录 特殊效果数据结构生成逻辑更新逻辑 文本渲染类结构构造函数加载函数渲染函数 特殊效果 为提高游戏的趣味性&#xff0c;在游戏中提供了六种特殊效果。 数据结构 PowerUp 类只存储存活数据&#xff0c;实际逻辑在游戏代码中通过Type字段来区分执行 class PowerUp …...

28. 【.NET 8 实战--孢子记账--从单体到微服务】--简易报表--报表定时器与报表数据修正

这篇文章是《.NET 8 实战–孢子记账–从单体到微服务》系列专栏的《单体应用》专栏的最后一片和开发有关的文章。在这片文章中我们一起来实现一个数据统计的功能&#xff1a;报表数据汇总。这个功能为用户查看月度、年度、季度报表提供数据支持。 一、需求 数据统计方面&…...

Java 泛型<? extends Object>

在 Java 泛型中&#xff0c;<? extends Object> 和 <?> 都表示未知类型&#xff0c;但它们在某些情况下有细微的差异。泛型的引入是为了消除运行时错误并增强类型安全性&#xff0c;使代码更具可读性和可维护性。 在 JDK 5 中引入了泛型&#xff0c;以消除编译时…...

FPGA|使用quartus II通过AS下载POF固件

1、将开发板设置到AS下载挡位&#xff0c;或者把下载线插入到AS端口 2、打开quartus II&#xff0c;选择Tools→Programmer→ Mode选择Active Serial Programming 3、点击左侧Add file…&#xff0c;选择 .pof 文件 →start 4、勾选program和verify&#xff08;可选&#xff0…...

“新月之智”智能战术头盔系统(CITHS)

新月人物传记&#xff1a;人物传记之新月篇-CSDN博客 相关文章链接&#xff08;更新&#xff09;&#xff1a; 星际战争模拟系统&#xff1a;新月的编程之道-CSDN博客 新月智能护甲系统CMIA--未来战场的守护者-CSDN博客 目录 一、引言 二、智能头盔控制系统概述 三、系统架…...

php:代码中怎么搭建一个类似linux系统的crontab服务

一、前言 最近使用自己搭建的php框架写一些东西&#xff0c;需要用到异步脚本任务的执行&#xff0c;但是是因为自己搭建的框架没有现成的机制&#xff0c;所以想自己搭建一个类似linux系统的crontab服务的功能。 因为如果直接使用linux crontab的服务配置起来很麻烦&#xff0…...

【LeetCode: 958. 二叉树的完全性检验 + bfs + 二叉树】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…...

MinDoc 安装与部署

下载可执行文件 mindoc mindoc_linux_amd64.zip 上传并解压压缩包 cd /opt mkdir mindoc cd mindocunzip mindoc_linux_amd64.zip 创建数据库 CREATE DATABASE mindoc_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; 配置数据库 将解压目录下 conf/app.conf.exam…...

从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架(基础组件实现)

目录 基础组件实现 如何将图像和文字显示到OLED上 如何绘制图像 如何绘制文字 如何获取字体&#xff1f; 如何正确的访问字体 如何抽象字体 如何绘制字符串 绘制方案 文本绘制 更加方便的绘制 字体附录 ascii 6x8字体 ascii 8 x 16字体 基础组件实现 我们现在离手…...

windows系统如何检查是否开启了mongodb服务

windows系统如何检查是否开启了mongodb服务&#xff01;我们有很多软件开发&#xff0c;网站开发时候需要使用到这个mongodb数据库&#xff0c;下面我们看看&#xff0c;如何在windows系统内排查&#xff0c;是否已经启动了本地服务。 在 Windows 系统上&#xff0c;您可以通过…...

VS安卓仿真器下载失败怎么办?

如果网络不稳定&#xff0c;则VS的安卓仿真器很容易下载失败&#xff0c;如下 Downloaded file <USER_HOME>\AppData\Local\Temp\xamarin-android-sdk\x86_64-35_r08.zip not found for Android SDK archive https://dl.google.com/android/repository/sys-img/google_a…...

计算机网络一点事(24)

TCP可靠传输&#xff0c;流量控制 可靠传输&#xff1a;每字节对应一个序号 累计确认&#xff1a;收到ack则正确接收 返回ack推迟确认&#xff08;不超过0.5s&#xff09; 两种ack&#xff1a;专门确认&#xff08;只有首部无数据&#xff09; 捎带确认&#xff08;带数据…...

视频拼接,拼接时长版本

目录 视频较长&#xff0c;分辨率较大&#xff0c;这个效果很好&#xff0c;不耗用内存 ffmpeg imageio&#xff0c;适合视频较短 视频较长&#xff0c;分辨率较大&#xff0c;这个效果很好&#xff0c;不耗用内存 ffmpeg import subprocess import glob import os from nats…...

制造企业的成本核算

一、生产成本与制造费用的区别 (1)生产成本,是直接用于产品生产,构成产品实体的材料成本。 包括企业在生产经营过程中实际消耗的原材料、辅助材料、备品备件、外购半成品、燃料、动力包装物以及其它直接材料,和直接参加产品生产的工人工资,以及按生产工人的工资总额和规…...

doris:高并发导入优化(Group Commit)

在高频小批量写入场景下&#xff0c;传统的导入方式存在以下问题&#xff1a; 每个导入都会创建一个独立的事务&#xff0c;都需要经过 FE 解析 SQL 和生成执行计划&#xff0c;影响整体性能每个导入都会生成一个新的版本&#xff0c;导致版本数快速增长&#xff0c;增加了后台…...

LLMs之WebRAG:STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略

LLMs之WebRAG&#xff1a;STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略 目录 STORM系统简介 1、Co-STORM 2、更新新闻 STORM系统安装和使用方法 1、安装 pip安装 直接克隆GitHub仓库 2、模型和数据集 两个数据集 FreshWiki数据集 WildSeek数据集 支持…...

鸿蒙HarmonyOS实战-ArkUI动画(页面转场动画)_鸿蒙arkui tab 切换动画

PageTransitionExit({type?: RouteType,duration?: number,curve?: Curve | string,delay?: number}) 在HarmonyOS中&#xff0c;PageTransitionEnter和PageTransitionExit是用于控制页面切换动画的参数。它们分别表示页面进入和退出时的动画。1. type&#xff08;动画类型…...

图漾相机-ROS2-SDK-Ubuntu版本编译(新版本)

文章目录 前言1.Camport ROS2 SDK 介绍1.1 Camport ROS2 SDK源文件介绍1.2 Camport ROS2 SDK工作流程1.2.1 包含头文件1.2.2 2 初始化 ROS 2 节点1.2.3 创建节点对象1.2.4 创建发布者对象并实现发布逻辑1.2.5 启动 ROS 2 1.3 ROS2 SDK环境配置与编译1.3.1 Ubuntu 20.04 下ROS2 …...

AI赋能开发:让快马智能分析并优化你的openclaw101风格网站代码与体验

今天想和大家分享一个很有意思的发现&#xff1a;用AI辅助开发工具来优化技术博客网站&#xff0c;效果真的超出预期。就拿我最近在InsCode(快马)平台上体验的openclaw101风格网站优化来说&#xff0c;整个过程既高效又有趣。 网站分析阶段 首先&#xff0c;我让平台的AI模型…...

保姆级图解:FD-SOI工艺流程中的关键三步(外延生长、应变硅、HKMG)

保姆级图解&#xff1a;FD-SOI工艺流程中的关键三步&#xff08;外延生长、应变硅、HKMG&#xff09; 在智能手机处理器和自动驾驶芯片的制造中&#xff0c;FD-SOI技术正凭借其独特的性能优势成为行业焦点。这项技术通过超薄绝缘层上硅&#xff08;Ultra-Thin Body and Buried…...

彩灯广告屏PLC控制S7-200程序:包含后发送产品梯形图、接线图原理图及IO分配与组态画面详解

彩灯广告屏的PLC控制S7-200程序 程序 我们主要的后发送的产品有&#xff0c;带解释的梯形图接线图原理图图纸&#xff0c;io分配&#xff0c;组态画面上周刚帮客户搞定了一套户外彩灯广告屏的PLC控制项目&#xff0c;用的还是经典的S7-200&#xff0c;本来以为老架构玩不出花…...

【Python原生AOT编译2026权威指南】:基于CPython 3.15+的零依赖二进制生成实战(含性能提升237%实测数据)

第一章&#xff1a;Python原生AOT编译的演进脉络与2026技术定位Python长期以来以解释执行和字节码&#xff08;.pyc&#xff09;为核心运行范式&#xff0c;其动态特性虽赋予开发极大灵活性&#xff0c;却在启动延迟、内存占用与部署包体积方面持续面临挑战。原生AOT&#xff0…...

Qwen3-0.6B-FP8极速对话工具:STM32F103C8T6最小系统板集成

Qwen3-0.6B-FP8极速对话工具&#xff1a;STM32F103C8T6最小系统板集成 让AI对话能力跑在指甲盖大小的开发板上 1. 场景与痛点 你可能很难想象&#xff0c;一个能进行智能对话的AI模型&#xff0c;居然可以运行在一块只有拇指大小的STM32开发板上。传统的AI模型部署往往需要强大…...

开箱即用版Sambert语音合成:多情感AI配音部署与使用

开箱即用版Sambert语音合成&#xff1a;多情感AI配音部署与使用 1. 引言&#xff1a;多情感语音合成的价值与挑战 在智能客服、有声读物、虚拟主播等应用场景中&#xff0c;富有情感表现力的语音合成技术正变得越来越重要。传统语音合成系统往往只能生成单调机械的语音&#…...

PyFluent:3大核心场景实现CFD仿真全流程自动化

PyFluent&#xff1a;3大核心场景实现CFD仿真全流程自动化 【免费下载链接】pyfluent 项目地址: https://gitcode.com/gh_mirrors/pyf/pyfluent 计算流体动力学&#xff08;CFD&#xff09;仿真作为工程设计的关键环节&#xff0c;长期面临流程繁琐、迭代低效、跨学科协…...

Suricata在CentOS7上的性能优化:如何配置网卡混杂模式与端口聚合

Suricata在CentOS7上的性能优化&#xff1a;网卡混杂模式与端口聚合实战指南 当企业网络流量突破千兆级别时&#xff0c;传统单网卡监控方案往往力不从心。我曾为某金融客户部署Suricata时&#xff0c;单台服务器每天要处理超过2TB的流量数据&#xff0c;正是通过下文介绍的网卡…...

从CCD到CMOS:HDR成像技术20年发展史与未来趋势

从CCD到CMOS&#xff1a;HDR成像技术20年演进与实战解析 在摄影器材展上&#xff0c;一位资深摄影师正用指尖轻抚不同年代的相机传感器——从2003年尼康D2H的CCD模块到2023年索尼A7RV的背照式CMOS&#xff0c;这个动作恰好勾勒出HDR技术演进的二十年轨迹。动态范围&#xff08;…...

避免踩坑:Unity中Resources.LoadAll的正确使用姿势(含multiple模式Sprite处理)

Unity资源加载进阶&#xff1a;Resources.LoadAll与Sprite图集高效处理指南 在Unity开发中&#xff0c;资源加载是每个项目都无法绕开的核心环节。特别是当处理包含多张小图的Sprite图集时&#xff0c;很多开发者会陷入性能陷阱和功能误区。本文将深入剖析Resources.LoadAll的正…...