【数据库】并发控制

并发控制

在数据库系统,经常需要多个用户同时使用。同一时间并发的事务可达数百个,这就是并发引入的必要性。

常见的并发系统有三种:

  • 串行事务执行(X),每个时刻只有一个事务运行,不能充分利用系统资源
  • 交叉并发(V),并行事务并行操作轮流交叉运行,适应于单处理机系统,能够减少处理机的空闲时间,提高系统的效率。
  • 同时并发,多个处理机同时运行多个事务,理想的并发方式,但是受限于硬件环境。
image-20210506153723551

但是并发控制可能导致一些问题,所以主要有三个任务:

  • 对并发操作的正确调度
  • 保证事务隔离性
  • 保证数据库的一致性

并发操作的后果

并发控制可能导致的数据不一致性有三类:

  • 丢失修改
  • 不可重复读
  • 读脏数据

为了说明这三种情况,我们用 R ( x ) R(x) R(x)表示读数据 x x x W ( x ) W(x) W(x)表示写数据 x x x.

一、丢失修改

考虑下面的情况:

T 1 T_1 T1 T 2 T_2 T2
R ( A ) = 16 R(A)=16 R(A)=16
R ( A ) = 16 R(A)=16 R(A)=16
A = A − 1 , W ( A ) = 15 A=A-1,W(A)=15 A=A1,W(A)=15
A = A − 1 A=A-1 A=A1, W ( A ) = 15 W(A)=15 W(A)=15

那么 T 1 T_1 T1 A A A的修改丢失了。

二、不可重复读

不可重复读是在 T 1 T_1 T1读取数据后, T 2 T_2 T2更新,导致 T 1 T_1 T1无法再现读取结果。

(1)RUR(Read, Update, Read)

T 1 T_1 T1 T 2 T_2 T2
R ( A ) = 50 , R ( B ) = 100 , S = 150 R(A)=50,R(B)=100, S=150 R(A)=50,R(B)=100,S=150
R ( B ) = 100 , W ( B ) = 200 R(B)=100, W(B)=200 R(B)=100,W(B)=200
R ( A ) = 50 , R ( B ) = 200 , S = 250 R(A)=50,R(B)=200, S=250 R(A)=50,R(B)=200,S=250

(2)RDR(Read, Delete, Read)

T 1 T_1 T1 T 2 T_2 T2
R ( A ) = 50 , R ( B ) = 100 , S = 150 R(A)=50,R(B)=100, S=150 R(A)=50,R(B)=100,S=150
将B记录从数据库中删除
无法读取到B的记录

(3)RAR(Read, Add, Read)

T 1 T_1 T1 T 2 T_2 T2
A中有两条记录, ∑ A i = 100 \sum A_i = 100 Ai=100
将记录50插入到集合A中
A中有三条记录, ∑ A i = 150 \sum A_i = 150 Ai=150

三、读脏数据

事务 T 1 T_1 T1修改某一数据,并写回磁盘,然后 T 2 T_2 T2读取之后, T 1 T_1 T1因为某种原因被撤销,这个时候 T 2 T_2 T2的数据可能不一致。

T 1 T_1 T1 T 2 T_2 T2
R ( C ) = 100 , W ( C ) = 200 R(C)=100, W(C)=200 R(C)=100,W(C)=200
R ( C ) = 200 R(C)=200 R(C)=200
ROLLBACK C,C恢复为100

封锁

一、封锁的概念

封锁是指事务在某个数据对象操作前先对系统请求进行加锁。加锁之后,事务就有了数据对象控制权。

基本封锁类型有两种:排它锁(Exclusive Locks, X锁)和共享锁(Share Locks, S锁)

排它锁又称写锁,如果 T T T A A A加X锁,那么其它事务不能加任何其他锁。这个时候,其它事务不能读取和修改

共享锁又称读锁,如果 T T T A A A加S锁,那么其它事务只能对 A A A S S S锁。这个时候,其它事务可以读 A A A,但是在 T T T释放锁之前不能进行修改。

换言之,存在锁的相容矩阵:

image-20210506161037958

二、封锁协议

申请锁、持有锁、释放锁的规则,叫做封锁协议。

一级封锁协议是事务中队数据修改之前必须对其加排它锁直到事务结束。

一级封锁协议可以有效防止丢失更新。

image-20210506163029740

二级封锁协议是在一级协议的基础上,要求读取数据之前必须加共享锁,读完再释放。这样可以防止读脏数据。

二级封锁协议可以有效防止读脏数据。

image-20210506163306517

三级封锁协议是在二级基础上,增加某事务施加的共享锁,保持到事务结束再释放。

三级封锁协议可以解决不可重复得问题。

image-20210506163505327

活锁和死锁

一、活锁

考虑有四个事务T1,T2,T3,T4

T1封锁数据R,T2请求R,所以T2等待。T3也请求R,然后T1释放R的锁之后系统调度给T3,T4请求R,T3释放R的锁之后系统调度给T4,导致T2永远等待。这就是活锁。

image-20210506163734548

避免活锁比较简单,只需要采取先来先服务的策略,在多个事务请求封锁同一个数据对象的时候按照请求封锁的先后次序对事务排序。

二、死锁

考虑两个事务T1、T2。T1封锁R1,T2封锁R2。T1此时请求封锁R2,而T2封锁R2,所以T1等待T2释放R2的锁。此时T2又申请R1,T1已经封锁R1,T2只能等待T1释放R1的锁。此时,T1、T2形成死锁。

image-20210506164033919

如果希望预防死锁,一般有两个思路:

  • 一次封锁法。要求每个事务必须一次将所有要使用的数据全部 加锁,否则就不能继续执行。但是这样比较难确定封锁对象,也会导致并发度降低。
  • 顺序封锁法。对数据对象规定封锁顺序,按照顺序进行封锁。但是这样难以确定事务要封锁哪些对象。

因此,死锁的预防比较难,多采用诊断解决的思路。

最简单的诊断方法就是使用超时法,如果事务等待时间超过规定时限,就说明发生死锁。这样实现简单,但是可能误判,并且如果时限过长,可能会让死锁无法及时发现。

等待图法是一个比较好的方式。设 G = ⟨ V , E ⟩ G=\langle V,E\rangle G=V,E V V V是正运行的事务, E E E是事务等待情况,如果 T 1 T_1 T1等待 T 2 T_2 T2,就连接 T 1 T 2 T_1T_2 T1T2。如果图中存在回路,说明系统出现死锁。

image-20210506164846270

检测到死锁后,选择一个处理死锁代价最小的事务,将其撤销。释放事务持有的所有锁,让其他事务能运行下去。

可串行性

多个事务的并发执行想要保证正确性,需要结果和某一次序串行执行结果相同。

一、可串行化的判定

可串行性是并发事务正确调度的准则,只有并发调度是可串行化的才能认为是正确调度。

考虑下面的两个事务:

  • T1:读B,A=B+1,写回A
  • T2:读A,B=A+1,写回B

对于下面的这些策略:

image-20210506170302306 image-20210506170339025

这两种策略相当于串行执行,因此并行执行的结果应当和二者之一相同。而对于下面的例子:

image-20210506170413777

不可串行化。

二、冲突可串行化

冲突可串行化给出了一个可串行化的充分条件:

一个调度Sc在保证冲突操作次序不变的情况下,通过交换两个事务不冲突操作的次序得到另一个调度Sc’。如果Sc’是串行的,称调度Sc为冲突可串行化的调度。

这里需要先引入冲突操作的概念。所谓冲突操作是指如下操作:

  • 事务Ti读x,Tj写x
  • 事务Ti写x,Tj写x

下面举一个例子。

证明:调度Sc1=r1(A)w1(A)r2(A)w2(A)r1(B)w1(B)r2(B)w2(B)可串行化。

r1(A)、w1(A)、w2(A)不可交换,r1(B)、w1(B)、w2(B)不可交换。

这样,可以交换为r1(A)w1(A)r1(B)w1(B)r2(A)w2(A)r2(B)w2(N)

这是一个串行调度T1T2,所以Sc1是冲突可串行化调度。

需要注意的是,这个条件并不是必要的。下面举一个反例。

T1=W1(Y)W1(X), T2=W2(Y)W2(X), T3=W3(X)

调度W1(Y)W2(Y)W2(X)W1(X)W3(X)结果与T1T2T3相同,可串行化,但并非冲突可串行化。

两段锁

在封锁的时候,对数据对象加锁需要遵守约定,比如何时申请加锁、锁持续时间和何时释放。两段封锁协议(2PL)是最常用的封锁协议,并且能产生可串行化调度。

两段锁协议是指所有事务需要分两个阶段对数据项加锁和解锁:

  • 在数据读写之前,事务需要先取得封锁
  • 释放封锁之后,事务不再申请和获得其它封锁

这里的两段,具体来说就是事务的两个阶段:

  • 扩展阶段,获得封锁,可以申请获得数据项上任何类型的锁,但是不能释放任何锁
  • 收缩阶段,释放封锁,可以释放任何数据线上的任何类型的锁,但是不能申请任何锁。

比如事务A按照两段锁协议的封锁序列是:

Slock A, Slock B, Xlock C, Unlock B, Unlock A, Unlock C

如果多个调度都符合两段锁协议,一定是一个可串行化调度。

image-20210506172305616

但是两段锁可能会出现死锁,所以还需要引入一次封锁法。也就是事务必须一次将所有使用数据全部加锁,否则就不能继续执行,这样可以回避死锁。

封锁粒度

封锁对象的大小称为封锁粒度。封锁对象分成逻辑单元和物理单元。

在关系数据库中,逻辑单元包括属性值、属性值集合、元组、关系、索引项、整个索引、整个数据库;物理单元包括页和物理记录。

封锁粒度和系统并发度与并发控制的开销密切相关。

  • 粒度大,封锁数据单元少,并发度低,开销小
  • 粒度小,并发度高,开销大

下面举两个例子。

(1)若封锁粒度是数据页,事务T1需要修改元组L1,则T1必 须对包含L1的整个数据页A加锁。如果T1对A加锁后事务T2要修改A中元组L2,则T2被迫等待,直到T1释放A。如果封锁粒度是元组,则T1和T2可以同时对L1和L2加锁,不需要互相等待,提高了系统的并行度。

(2)事务T需要读取整个表,若封锁粒度是元组,T必须对表中的每一个元组加锁,开销极大。

因此,在一个系统中需要同时支持多种封锁粒度供不同事务选择,也就是 多粒度封锁。在选择粒度的时候,要同时考虑封锁开销和并发度:

  • 对处理多个关系大量元组的事务,以数据库为封锁单位
  • 对处理大量元组的事务,以关系为封锁单位
  • 对少量元组的用户事务,以元组为封锁单位。

可以用一颗树来表示粒度,称作多粒度树:

image-20210506173117734

在这个树中,对一个结点加锁相当于节点所有子孙加同类型的锁。这里就引入了显式封锁和隐式封锁:显式封锁是直接加到数据对象上的封锁,隐式封锁是由于上级结点加锁导致的子节点加锁。

因此,系统在检查封锁冲突的收,需要检查显式封锁和隐式封锁。具体来说就是:

  • 数据对象有没有显式封锁与之冲突
  • 本事务显式封锁是否与上级节点隐式封锁冲突
  • 上面的显式封锁是否与本事务隐式封锁冲突

意向锁

在此基础上,引入意向锁,来提高对某个数据对象加锁时系统的检查效率。

如果对一个结点加意向锁,说明其下层节点正在被加锁;对任意结点加基本锁,必须对上层节点加意向锁。

常用的意向锁有三种:

  • 意向共享锁(IS锁),表示后裔节点拟(意向)加S锁
  • 意向排它锁(IX锁),表示后裔节点拟(意向)加X锁
  • 共享意向排它锁(SIX锁),表示对它加S锁,再加IX锁。

对于这些锁,相容矩阵如下:

image-20210513153049258

锁强度的哈斯图如下:

image-20210513153118269

这里锁强度是对其他所的排斥程度,强锁对弱锁是安全的。

在引入意向锁之后,执行封锁操作:

  • 申请时按照从上到下的次序
  • 释放时按照从下到上的次序

例如,T1对R1加S锁,需要下面的操作

  • 对数据库加IS锁
  • 检查数据库和R1是否加了不相容锁,也就是X或IX锁
  • 无需搜索R1中元组是否加了X锁。

这样,意向锁提高了系统并发度,减少了加减锁的开销,得到广泛应用。

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

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

相关文章

我们来学mysql -- “数据备份还原”sh脚本

数据备份&还原 说明执行db_backup_cover.sh脚本 说明 环境准备:来源数据库(服务器A);目标数据库(服务器B)dbInfo.sh脚本记录基本信息 来源库、目标库的ip、port及执行路径 # MySQL 客户端和 mysqldump 的路径 MYSQL_CLIENT"/work/oracle/mysql…

【NLP 78、手搓Transformer模型结构】

你以为走不出的淤泥,也迟早会云淡风轻 —— 25.5.31 引言 ——《Attention is all you need》 《Attention is all you need》这篇论文可以说是自然语言处理领域的一座里程碑,它提出的 Transformer 结构带来了一场技术革命。 研究背景与目标 在 Transfo…

深入理解CSS常规流布局

引言 在网页设计中,理解元素如何排列和相互作用至关重要。CSS提供了三种主要的布局方式:常规流、浮动和定位。本文将重点探讨最基础也是最常用的常规流布局(Normal Flow),帮助开发者掌握页面布局的核心机制。 什么是…

树结构详细介绍(javascript版)

树结构的基本概念 树是一种非线性数据结构,由节点和连接节点的边组成。与线性数据结构(如数组、链表)不同,树具有层次结构,非常适合表示有层次关系的数据。 树的基本术语 节点 (Node): 树中的基本单元&a…

element-plus bug整理

1.el-table嵌入el-image标签预览时&#xff0c;显示错乱 解决&#xff1a;添加preview-teleported属性 <el-table-column label"等级图标" align"center" prop"icon" min-width"80"><template #default"scope"&g…

RabbitMQ和MQTT区别与应用

RabbitMQ与MQTT深度解析&#xff1a;协议、代理、差异与应用场景 I. 引言 消息队列与物联网通信的重要性 在现代分布式系统和物联网&#xff08;IoT&#xff09;生态中&#xff0c;高效、可靠的通信机制是构建稳健、可扩展应用的核心。消息队列&#xff08;Message Queues&am…

零基础远程连接课题组Linux服务器,安装anaconda,配置python环境(换源),在服务器上运行python代码【3/3 适合小白,步骤详细!!!】

远程连接服务器 请查阅之前的博客——零基础远程连接课题组Linux服务器&#xff0c;安装anaconda&#xff0c;配置python环境&#xff08;换源&#xff09;&#xff0c;在服务器上运行python代码【1/3 适合小白&#xff0c;步骤详细&#xff01;&#xff01;&#xff01;】&am…

Redis最佳实践——安全与稳定性保障之访问控制详解

Redis 在电商应用的安全与稳定性保障之访问控制全面详解 一、安全访问控制体系架构 1. 多层级防护体系 #mermaid-svg-jpkDj2nKxCq9AXIW {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-jpkDj2nKxCq9AXIW .error-ico…

vue2源码解析——响应式原理

文章目录 引言数据劫持收集依赖数组处理渲染watchervue3中的响应式 引言 vue的设计思想是数据双向绑定、数据与UI自动同步&#xff0c;即数据驱动视图。 为什么会这样呢&#xff1f;这就不得不提vue的响应式原理了&#xff0c;在使用vue的过程中&#xff0c;我被vue的响应式设…

gcc相关内容

gcc 介绍&#xff1a;linux就是由gcc编译出来的&#xff0c;而且好像之前Linux只支持gcc编译。gcc全称为gnu compiler collection&#xff0c;它是gnu项目的一个组成部分。gnu致力于创建一个完全自由的操作系统&#xff0c;我感觉意思就是完全开源的操作系统。gnu有很多组件和…

android 图片背景毛玻璃效果实现

图片背景毛玻璃效果实现 1 依赖 // Glide implementation("com.github.bumptech.glide:glide:4.16.0") kapt("com.github.bumptech.glide:compiler:4.16.0") implementation("jp.wasabeef:glide-transformations:4.3.0") 2 布局<com.googl…

【Java开发日记】你会不会5种牛犇的yml文件读取方式?

前言 除了烂大街的Value和ConfigurationProperties外&#xff0c;还能够通过哪些方式&#xff0c;来读取yml配置文件的内容&#xff1f; 1、Environment 在Spring中有一个类Environment&#xff0c;它可以被认为是当前应用程序正在运行的环境&#xff0c;它继承了PropertyReso…

Spring Boot事务失效场景及解决方案

事务失效场景1&#xff1a;方法非public修饰 原因 Spring事务基于动态代理&#xff08;AOP&#xff09;实现&#xff0c;非public方法无法被代理拦截&#xff0c;导致事务失效。 代码示例 Service public class OrderService {Transactionalprivate void createOrder() { //…

电子电路:怎么理解时钟脉冲上升沿这句话?

时钟脉冲是数字电路中用于同步各组件操作的周期性信号&#xff0c;通常表现为高低电平交替的方波。理解其关键点如下&#xff1a; 时钟脉冲的本质&#xff1a; 由晶振等元件生成&#xff0c;呈现0/1&#xff08;低/高电平&#xff09;的规律振荡每个周期包含上升沿→高电平→下…

docker部署redis mysql nacos seata rabbitmq minio onlyoffice nginx实战

docker部署redis mysql nacos seata rabbitmq minio onlyoffice nginx实战 一、环境介绍 操作系统&#xff1a;ubuntu22.04 软件环境&#xff1a;docker、docker-compose 二、docker安装 版本规定到26.1.3版本过低会引起莫名其妙的问题。打开终端。更新软件包列表&#x…

全面解析:npm 命令、package.json 结构与 Vite 详解

全面解析&#xff1a;npm 命令、package.json 结构与 Vite 详解 一、npm run dev 和 npm run build 命令解析 1. npm run dev 作用&#xff1a;启动开发服务器&#xff0c;用于本地开发原理&#xff1a; 启动 Vite 开发服务器提供实时热更新&#xff08;HMR&#xff09;功能…

【Oracle】TCL语言

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. TCL概述1.1 什么是TCL&#xff1f;1.2 TCL的核心功能 2. 事务基础概念2.1 事务的ACID特性2.2 事务的生命周期 3. COMMIT语句详解3.1 COMMIT基础语法3.2 自动提交与手动提交3.3 提交性能优化 4. ROLLBACK语句…

OpenCV CUDA模块直方图计算------用于在 GPU 上执行对比度受限的自适应直方图均衡类cv::cuda::CLAHE

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::CLAHE 是 OpenCV 的 CUDA 模块中提供的一个类&#xff0c;用于在 GPU 上执行对比度受限的自适应直方图均衡&#xff08;Contrast Limi…

OpenGAN:基于开放数据生成的开放集识别

简介 简介&#xff1a;这次学习的OpenGAN主要学习一个思路&#xff0c;跳出传统GAN对于判断真假的识别到判断是已知种类还是未知种类。重点内容不在于代码而是思路&#xff0c;会简要给出一个设计的代码。 论文题目&#xff1a;OpenGAN: Open-Set Recognition via Open Data …

随机游动算法解决kSAT问题

input&#xff1a;n个变量的k-CNF公式 ouput&#xff1a;该公式的一组满足赋值或宣布没有满足赋值 算法步骤&#xff1a; 随机均匀地初始化赋值 a ∈ { 0 , 1 } n a\in\{0,1\}^n a∈{0,1}n.重复t次&#xff08;后面会估计这个t&#xff09;&#xff1a; a. 如果在当前赋值下…