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

VLL: a lock manager redesign for main memory database systems阅读

为何要有VLL?VLL旨在解决什么问题?

在数据库系统中,锁是广泛使用的并发控制机制。然而对于内存数据库系统,锁管理器却成为了性能瓶颈所在。

一项研究说明内存数据库中有16%~25%的时间用于与锁管理器的交互

在传统的锁管理器中,最常见的实现方式是采用以数据主键为id,请求该数据的锁请求链表为值的hash表,锁请求链表还存在锁头追踪该项的锁状态,以及支持互斥操作。

对于与磁盘相关的数据库,这些hash表查找、锁获取和链表操作都是可以忽略不计的,可对于内存数据库来说,这些额外的内存访问、cache未命中、cpu周期、以及临界区就可能接近或者超过执行实际事务逻辑的成本

VLL在上述锁管理器上做出了两个主要变化

  • 将锁信息从中心hash表中移出到原始数据中
  • 从锁数据结构中删除哪些事务对特定锁未完成请求的信息,取而代之的是原始数据包含未完成的写、读请求的数量(CX记录未完成的写请求数量、CS记录未完成的读请求数量)

在事务获取锁时,会一次请求所有锁,并按照请求锁的顺序对事务排序,按照这种全局事务顺序来确定哪个事务应该先解除堵塞并执行

可是VLL也并非银弹,在高争用负载下,可能会导致并发减少和CPU利用率差。为解决这个问题,又引入了选择性竞争优化(SCA),在需要的时候高效计算最有用的竞争信息子集

VLL算法介绍

锁结构

VLL锁管理结构由

  • 储存到每个原始数据的的未完成锁写请求数量CX和未完成锁读请求数量CS
  • 中心事务请求全局队列TxnQueue

组成

流程

具体流程如下

  1. 当事务到达分区时,会尝试对该分区所有在其生命周期内访问的记录加锁

    每个锁请求都采取相应记录的CX或CS加1的形式

  2. 如果请求后的CX=1且CS=0,则认为独占锁被请求事务抢占

    如果请求后的CX=0,则认为请求事务获取了共享锁

  3. 当事务获取锁之后就被添加到TxnQueue

    请求锁和向队列添加事务都发生在同一临界区中

  4. 在离开临界区时,若事务在请求时获取到所有锁,则该事务为free,否则为堵塞。那么只有free事务才能够立即执行

问题

考虑以下问题

  1. 单线程VLL在需要其他节点协作的事务请求中,如果一直等待,会导致资源的浪费
  2. 在没有中心锁管理结构情况下,如何释放等待中的堵塞事务执行呢?
  3. 如果在CX=0的情况下才能获取锁,而写请求到达之后都能递增CX,那么在存在多个写请求事务堵塞情况下,CX会永远大于0
  4. 随着TxnQueue大小增长,新事务能立刻获取锁的概率大大降低

为解决这些问题

  1. 引入waiting状态,标识事务正在进行中,但需要获取搭配远程结果才能继续执行。因此在进入waiting状态之后就可以挂起,不会导致额外的资源浪费
  2. 让后台线程检查TxnQueue中每个堵塞事务的请求数据CX、CS值,若CX下降到1,CS为0,则说明该事务获取到互斥锁;CX为0,CS大于0,则说明获取到共享锁
  3. 对于队列头的堵塞事务直接解除堵塞并执行。这是因为事务位于队列头,说明在此之前请求锁的事务都已经完结了
  4. 为TxnQueue设立阈值,当超过后,将会停止处理新事务,从TxnQueue中寻找可以被解除堵塞的事务。而在阈值内则会优先处理新请求事务
代码及流程展示

以下是代码展示

func beginTransaction(t *TxnItem) {// 开始时,事务状态为Freet.TxnStatus = Free// 遍历本地读集合,检查是否存在冲突,若存在冲突,则事务状态为Blockedfor _, readKey := range t.ReadSet {if inLocal(readKey) {data[readKey].cs++if data[readKey].cx > 1 {t.TxnStatus = Blocked}}}// 遍历本地写集合,检查是否存在冲突,若存在冲突,则事务状态为Blockedfor _, writeKey := range t.WriteSet {if inLocal(writeKey) {data[writeKey].cx++if data[writeKey].cs > 0 || data[writeKey].cx > 1 {t.TxnStatus = Blocked}}}// 若事务状态为Free,且事务为分布式事务,则事务状态为Waitingif t.TxnType == TxnTypeDistributed && t.TxnStatus == Free {t.TxnStatus = Waiting}
}func commitTransaction(t *TxnItem) {// 提交事务时,释放读集合的锁for _, readKey := range t.ReadSet {if inLocal(readKey) {data[readKey].cs--}}// 提交事务时,释放写集合的锁for _, writeKey := range t.WriteSet {if inLocal(writeKey) {data[writeKey].cx--}}
}func vll() {for {// 若接收到远程事务消息,则处理该事务的消息,并执行事务、提交事务以及从队列中移除该事务t, ok, message := selectReceivedMessageTx()if ok {handleReadResult(t, message)if isReadyToExecute(t) {execute(t)commitTransaction(t)txnQueue.dequeue(t)}continue}// 若队列头事务堵塞,在分布式事务时,设置状态为等待,并给其他参与节点发送事务请求// 否则,设置状态为空闲,执行事务、提交事务以及从队列中移除该事务if txnQueue.front().TxnStatus == Blocked {t = txnQueue.front()if t.TxnType == TxnTypeDistributed {t.TxnStatus = Waiting// 给其他参与节点发送事务请求} else {// 注意!此处,对于队列头的堵塞事务直接解除堵塞并执行// 这是因为事务位于队列头,说明在此之前请求锁的事务都已经完结了。并且考虑到存在多个事务增加同数据CX值的情况,使得CX总是会大于1,导致死锁t.TxnStatus = Freeexecute(t)commitTransaction(t)txnQueue.dequeue(t)}} else if !txnQueue.isFull() {// 若队列未满,则获取新事务请求// 新事务状态为Free,执行事务、提交事务// 若事务状态为Waiting,给其他参与节点发送事务请求,并入队// 若事务状态为Blocked,入队// 注意!检查未满的缘故在于平衡新旧事务的处理。在阈值内优先处理新事务请求,阈值之外优先处理waiting的事务t = getNewTxnRequest()beginTransaction(t)switch t.TxnStatus {case Free:execute(t)commitTransaction(t)case Waiting:// 给其他参与节点发送事务请求txnQueue.enqueue(t)case Blocked:txnQueue.enqueue(t)}}}
}

考虑下面场景下的演变,表中展示的是各事务请求的数据

txnread setwrite set
Ax, yx
Bx, yx
Cx0
Dyz
E0y
  1. 最开始,所有数据的CX、CS都是0,TxnQueue为空
  2. 事务A发起读取x、y,并写入x请求,此时数据x的CX递增,数据y的CS递增,并且事务A作为free事务加入TxnQueue
  3. 事务B发起x、y读,x写的请求。当由于x.CX>0,所以B无法获取到x的写锁,因此作为堵塞事务加入到TxnQueue
  4. 当A完成执行后,释放锁。此时x.CX–、y.CS–,并将A从TxnQueue移除,B而后标记为free,获取锁开始执行
  5. 事务C发起读取x的请求,因此x.CS++,由于x.CX>0,因此事务C的读请求无法保证,作为堵塞事务加入到TxnQueue
  6. 事务D发起对y读请求,对z写请求。那么y.CS++,z.CX=1,由于y.CX和z.CX此前均为0,事务D所有锁获取成功,并作为free事务加入TxnQueue
  7. 事务E发起对y的写请求,由于y.CS>0,所以E请求失败,作为堵塞事务加入到TxnQueue
  8. 事务B执行完毕后,释放x写锁、y读锁,并从TxnQueue移除,C此时位于TxnQueue队列最前面,因此标记为free,成功获取到x读锁并执行
  9. 最后事务E也能够安全执行,因为不与TxnQueue中在其之前的任何事务冲突。但又由于VLL算法仅允许TxnQueue最前面事务执行,所以还是不能够执行。SCA将解除该限制
array VLL

array VLL采用向量或数组的数据结构来连续储存锁信息,这会使得数据和锁信息是在两次单独请求中进行的,但在单独线程中,由于在核心缓存中,所以也能在性能上有着较好的发挥

一次性获取所有锁的障碍
  • 在运行事务前,事务读写集可能是未知的
  • 由于每个节点都有TxnQueue,并且临界区在本地,那么不同的节点可能处理顺序并不一致,这可能会导致死锁

为解决第一个问题,允许事务在进入临界区前,能给执行所需要的任何读取

为解决节点事务顺序不一致引发的死锁问题,有两种解决方法

  • 允许产生死锁,通过使用死锁检测协议取消死锁事务
  • 协调节点,使各节点事务顺序一致

VLL采用协调节点的方式,可以在增加协调开销的情况下,仍然产生改进的性能

SCA——提升在高争用下的性能

对于高竞争和高百分比的多分区工作负载,VLL引入了SCA,进行模拟标准锁管理器去检测哪些事务应该继承已释放锁的能力

SCA只在真正需要的时候才会激活,比如在CPU处理空闲时。从队列头往尾部进行扫描,分析在事务之前的事务是否存在冲突

func sca(q TxnQueue) *TxnItem {// 初始化冲突检测数组dx := make([]int8, 100<<10)ds := make([]int8, 100<<10)for _, item := range q {// 如果事务被阻塞if item.TxnStatus == Blocked {success := true// 检查读集合,若读集合中的数据已经被写占用,则事务失败for _, read := range item.ReadSet {key := hash(read)if dx[key] == 1 {success = false}// 读集合中的数据标记为已占用ds[key] = 1}// 检查写集合,若写集合中的数据已经被读、写占用,则事务失败for _, write := range item.WriteSet {key := hash(write)if ds[key] == 1 || dx[key] == 1 {success = false}// 写集合中的数据标记为已占用dx[key] = 1}// 若事务成功,则返回该事务if success {return item}}else {// 事务未被阻塞,仅标记读写集合数据被占用for _, read := range item.ReadSet {key := hash(read)ds[key] = 1}for _, write := range item.WriteSet {key := hash(write)dx[key] = 1}}}return nil
}

VLLR——支持范围的VLL

对于频繁读取、写入、删除同一事务中的许多连续行,锁定行范围比锁定多个单独行更有用,可以避免幻读以及在删除一个数据行共享其主键的公共前缀的实体的常见用例中特别有用

VLLR通过锁定位串前缀进行范围锁定。当锁定一个范围主键时,将该范围表示为集合R,然后生成集合P,使得R中任何一个元素都能在P中找到前缀。最简单生成P的方式时选择R中最小和最大位串的最长公共前缀

在得到集合P之后,VLLR采用分层锁的方式进行。即先从数据库获取意图锁,而后从表获取意图锁,接着在页获取意图锁,最后获取行锁

此外,为了更好管理锁状态,VLLR在CX、CS的基础上,引入了P集合的LX、LS计数器,也就是如果当前请求属于集合P的写请求,则LX递增,属于集合P的读请求,则LS递增

func requestLock(item *TxnItem, rg *txnRange, exclusive bool) bool {// 事务加入队列txnQueue.enqueue(item)// 获取锁范围的前缀pfs := rg.toPrefix()// 请求前缀锁success := truefor _, pf := range pfs {success = success && requestPrefixLock(pf, exclusive)}return success
}func requestPrefixLock(key string, exclusive bool) bool {success := truek := hash(key)// 检查当前key数据锁是否不可获取if exclusive {success = success && (cs[k] == 0)}success = success && (cx[k] == 0)// 检查key数据所在前缀锁是否不可获取if exclusive {success = success && (ls[k] == 0)}success = success && (lx[k] == 0)// 检查key所有前缀的数据锁是否不可获取for i := 1; i < len(key); i++ {h := hash(key[:i])success = success && (cx[h] == 0)if exclusive {success = success && (cs[h] == 0)}}// 标记key数据锁if exclusive {cx[k]++} else {cs[k]++}// 标记key数据所有前缀的前缀锁for i := 1; i < len(key); i++ {h := hash(key[:i])if exclusive {lx[h]++} else {ls[h]++}}return success
}func releaseLock(item *TxnItem, rg *txnRange, exclusive bool) {// 释放锁范围的前缀pfs := rg.toPrefix()for _, pf := range pfs {releasePrefixLock(pf, exclusive)}// 事务出队列txnQueue.dequeue(item)
}func releasePrefixLock(key string, exclusive bool) {// 释放key数据锁k := hash(key)if exclusive {cx[k]--} else {cs[k]--}// 释放key数据所有前缀的前缀锁for i := 1; i < len(key); i++ {h := hash(key[:i])if exclusive {lx[h]--} else {ls[h]--}}
}

Ref

  1. https://www.cs.umd.edu/~abadi/papers/vldbj-vll.pdf

相关文章:

VLL: a lock manager redesign for main memory database systems阅读

为何要有VLL&#xff1f;VLL旨在解决什么问题&#xff1f; 在数据库系统中&#xff0c;锁是广泛使用的并发控制机制。然而对于内存数据库系统&#xff0c;锁管理器却成为了性能瓶颈所在。 一项研究说明内存数据库中有16%&#xff5e;25%的时间用于与锁管理器的交互 在传统的锁…...

REST API实战演练之JavaScript使用Rest API

咱们前面讲了一下如何创建REST API 假期别闲着&#xff1a;REST API实战演练之创建Rest API-CSDN博客 又讲了java客户端如何使用REST API 假期别闲着&#xff1a;REST API实战演练之客户端使用Rest API-CSDN博客 接下来咱们看看JavaScript怎么使用REST API。 一、新建一个…...

期货量化交易软件:MQL5 中的范畴论 (第 15 部分)函子与图论

概述 在上一篇文章中&#xff0c;我们目睹了前期文章中涵盖的概念&#xff08;如线性序&#xff09;如何视作范畴&#xff0c;以及为什么它们的“态射”在与其它范畴相关时即构成函子。在本文中&#xff0c;我们赫兹量化软件将阐述来自前期文章中的概括&#xff0c;即通过查看…...

2024妈妈杯数学建模B题思路-甲骨文智能识别中原始拓片单字自动分割与识别研究

# 1 赛题 B 题 甲骨文智能识别中原始拓片单字自动分割与识别研究 甲骨文是我国目前已知的最早成熟的文字系统&#xff0c;它是一种刻在龟甲或 兽骨上的古老文字。甲骨文具有极其重要的研究价值&#xff0c;不仅对中国文明的 起源具有重要意义&#xff0c;也对世界文明的研究有着…...

sql 之 索引

索引是对数据库表中一列或多列的值进行排序的一种结构&#xff0c;使用索引可快速访问数据库表中的特定信息。 1. 什么是索引 官方上面说索引是帮助MySQL高效获取数据的数据结构&#xff0c;通俗点来说&#xff0c;数据库索引就像是是一本书的目录&#xff0c;可以直接根据页码…...

创建基于Node的WebSocket服务

一:安装nodejs与npm apt-get install nodejs npm 但这种方法安装的版本可能偏低,影响后续的 npm install ws wscat。 按照 How to Install Node.js and npm on Ubuntu 18.04 | Linuxize里的步骤安装: 1、curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash …...

Flask快速搭建文件上传服务与接口

说明&#xff1a;仅供学习使用&#xff0c;请勿用于非法用途&#xff0c;若有侵权&#xff0c;请联系博主删除 作者&#xff1a;zhu6201976 一、需求背景 前端通过浏览器&#xff0c;访问后端服务器地址&#xff0c;将目标文件进行上传。 访问地址&#xff1a;http://127.0.0…...

AI算力报告:算力大时代,AI算力产业链全景梳理

今天分享的是AI算力专题系列深度研究报告&#xff1a;《算力大时代&#xff0c;AI算力产业链全景梳理》。 &#xff08;报告出品方&#xff1a;中信建投证券&#xff09; 报告共计&#xff1a;98页 核心观点 生成式 AI取得突破&#xff0c;我们对生成式 A 带来的算力需求做…...

点击上传文件

一、页面样式&#xff1a; &#xff08;1&#xff09;点击前&#xff1a; &#xff08;2&#xff09;点击后&#xff1a; 设计&#xff1a;①自定义elementPlus图标&#xff1b;②使用Tooltip实现鼠标悬浮按钮上出现文字提示&#xff1b;③上传与更换的切换样式&#xff1b;…...

文件上传【2】--靶场通关

1.前端禁用js绕过 上传文件&#xff0c;进行抓包&#xff0c;没有抓到&#xff0c;说明这里的验证是前端js验证跳出的弹窗 禁用js后&#xff0c;php文件上传成功。 2.文件上传.htaccess 上传png木马后连接不上 代码中存在.htaccess&#xff0c;判断此时应该就是需要用到.htac…...

uniapp请求后端接口

新建文件夹utils const request (config) > {// 拼接完整的接口路径config.url http://mm.test.cn config.url;//这里拼接的是访问后端接口的地址&#xff0c;http://mm.test.cn/prod-api/testconsole.log(config.url)//判断是都携带参数if(!config.data){config.data …...

第十三章 OpenGL ES-RGB、HSV、HSL模型介绍

第十三章 OpenGL ES-RGB、HSV、HSL模型详细介绍 第一章 OpenGL ES 基础-屏幕、纹理、顶点坐标 第二章 OpenGL ES 基础-GLSL语法简单总结 第三章 OpenGL ES 基础-GLSL渲染纹理 第四章 OpenGL ES 基础-位移、缩放、旋转原理 第五章 OpenGL ES 基础-透视投影矩阵与正交投影矩阵…...

微软卡内基梅隆大学:无外部干预,GPT4等大语言模型难以自主探索

目录 引言&#xff1a;LLMs在强化学习中的探索能力探究 研究背景&#xff1a;LLMs的在情境中学习能力及其重要性 实验设计&#xff1a;多臂老虎机环境中的LLMs探索行为 实验结果概览&#xff1a;LLMs在探索任务中的普遍失败 成功案例分析&#xff1a;Gpt-4在特定配置下的探…...

探索设计模式的魅力:简单工厂模式

个人主页: danci_ &#x1f525;系列专栏&#xff1a;《设计模式》《MYSQL应用》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#xff1a;探索设计模式的魅力&#xff1a;简单工厂模式 简单工厂模式&#x…...

【数据结构】-----双链表(小白必看!!!)

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…...

【数据结构】考研真题攻克与重点知识点剖析 - 第 8 篇:排序

前言 本文基础知识部分来自于b站&#xff1a;分享笔记的好人儿的思维导图与王道考研课程&#xff0c;感谢大佬的开源精神&#xff0c;习题来自老师划的重点以及考研真题。此前我尝试了完全使用Python或是结合大语言模型对考研真题进行数据清洗与可视化分析&#xff0c;本人技术…...

数字乡村可视化大数据-DIY拖拽式设计

DIY拖拽式大数据自由设计万村乐可视化大数据V1.0 随着万村乐数字乡村系统的广泛使用&#xff0c;我们也接收到了客户的真实反馈&#xff0c;最终在公司的决定下&#xff0c;我们推出了全新的可视化大数据平台V1.0版本&#xff0c;全新的可视化平台是一个通过拖拽配置生成可视化…...

数据集学习

1&#xff0c;CIFAR-10数据集 CIFAR-10数据集由10个类的60000个32x32彩色图像组成&#xff0c;每个类有6000个图像。有50000个训练图像和10000个测试图像。 数据集分为五个训练批次和一个测试批次&#xff0c;每个批次有10000个图像。测试批次包含来自每个类别的恰好1000个随机…...

【解决】npm run dev Syntax Error: TypeError: eslint.CLIEngine is not a constructor

问题&#xff1a; 由于代码语法不符合eslint而照成此错误&#xff0c;可以参照eslint规则修改语法&#xff0c;或者将eslint停掉 以下为停掉eslint的方法。 You may use special comments to disable some warnings. Use // eslint-disable-next-line to ignore the ne…...

Android 如何通过屏幕大小来适配不同大小的图片

可以使用Android中的dp(密度无关像素)单位来设置不同屏幕密度下的图片大小。dp是Android中的一种尺寸单位&#xff0c;它与屏幕密度无关&#xff0c;只与字体大小有关。在开发过程中&#xff0c;可以使用dp来设置布局和控件的大小&#xff0c;以便在不同的屏幕密度下保持一致的…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析&#xff08;97/126&#xff09;&#xff1a;邮件营销与用户参与度的关键指标优化指南 在数字化营销时代&#xff0c;邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天&#xff0c;我们将深入解析邮件打开率、网站可用性、页面参与时…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…...

LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用

中达瑞和自2005年成立以来&#xff0c;一直在光谱成像领域深度钻研和发展&#xff0c;始终致力于研发高性能、高可靠性的光谱成像相机&#xff0c;为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...

[USACO23FEB] Bakery S

题目描述 Bessie 开了一家面包店! 在她的面包店里&#xff0c;Bessie 有一个烤箱&#xff0c;可以在 t C t_C tC​ 的时间内生产一块饼干或在 t M t_M tM​ 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC​,tM​≤109)。由于空间…...

归并排序:分治思想的高效排序

目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法&#xff0c;由约翰冯诺伊曼在1945年提出。其核心思想包括&#xff1a; 分割(Divide)&#xff1a;将待排序数组递归地分成两个子…...

DAY 45 超大力王爱学Python

来自超大力王的友情提示&#xff1a;在用tensordoard的时候一定一定要用绝对位置&#xff0c;例如&#xff1a;tensorboard --logdir"D:\代码\archive (1)\runs\cifar10_mlp_experiment_2" 不然读取不了数据 知识点回顾&#xff1a; tensorboard的发展历史和原理tens…...