事务可串行化原理

本文围绕数据库事务展开,介绍了事务的组成和 ACID 特性。阐述了可串行化调度,指出为充分利用系统资源应允许事务并发调度,判断并发调度正确的标准是可串行化。还说明了冲突操作、冲突等价、冲突可串行化等概念及判断方式,如用优先图判断冲突可串行化。

访问并可能更新各种数据项的一个程序执行单元。通常由高级数据库操作语言或编程语言通过 JDBC 或 ODBC 嵌入式数据库访问书写的用户程序的执行所引起的。

【组成】:事务开始(begin transaction)和事务结束(end transaction)之间执行的全体操作。

【ACID 特性】:

  • Atomicity:原子性,事务是一个不可分割的工作单位,事务中包括的操作要么都执行,要么都不执行。
  • Consistency:隔离性,隔离执行事务时(即在没有其他事务并发执行时)保持数据库的一致性。
  • Isolation:独立性,多事务执行,系统保证对于任何一对事务 A 与 B 都感知不到对方的存在。
  • Durability:持久性,事务执行成功,对数据库的改变必须是永久的,即使数据库出现故障。

可串行化

如下图所示,T1、T2 的执行顺序称为调度(schedule),它们表示指令在系统中执行的时间顺序。一组事务的一个调度必须包含这一组事务的全部指令,并且必须保持指令在当前事务中出现的顺序。

T1          T2
Read(A);
A = A - 50;
Write(A);
Read(B);
B = B + 50;
Write(B);
            Read(A);
            Temp = A * 0.1;
            A = A - Temp;
            Write(A);
            Read(B);
            B = B + Temp;
            Write(B);

事务 T1 先于 T2(或 T2 先于 T1)执行,则称之为串行调度。n 个事务的事务组,共有 n! 个不同的有效串行调度。

假设 n = 4,T1、T2 和 T3,3!= 6。

T1 T2 T3
T1 T3 T2
T2 T1 T3
T2 T3 T1
T3 T1 T2
T3 T2 T1

若事务只采取串行调度,则其他事务必须等到当前事务结束后运行。事务在执行过程中需要不同的资源,CPU、存取数据库以及 I/O 除操作。如果事务串行执行,则许多系统资源将处于空闲状态。因此,为了充分利用系统资源,应该允许事务并发调度

那么如何判断并发调度是正确的?某个并发调度,当且仅当它是可串行化的,即正确调度可串行化调度又是什么?

可串行化调度

多个事务的并发执行是正确的,当且仅当其结果与按某一次序串行执行这些事务时的结果相同。可串行化调度能保证并发的事务调度方式既能满足数据一致性需求,又能提高并发事务的执行效率。

【示例】:考虑调度 S,其中两条连续指令 I 和 J,如果 I 与 J 引用不同的数据项,则交换 I 和 J 不影响调度中任何指令的结果。如果 I 和 J 引用相同的数据项 Q,会出现以下四种情景:

  1. I = read(Q) , J = read(Q) 次序不重要,Ti与Tj取值相同
  2. I = read(Q) , J = write(Q) 影响另外的事务读的值
  3. I = write(Q), J = read(Q) 影响另外的事务读的值
  4. I = write(Q), J = write(Q) 下一条read会受影响,即影响的数据库最终值

冲突操作是指不同事务对同一个数据的读写操作和写写操作。判断规则如下:

  • 两个动作属于不同的事务;
  • 至少一个动作是写操作;
  • 动作在操作同一个对象(读同一个对象或写同一个对象)。

上述示例中:非冲突:1,冲突:2、3、4。

【冲突等价(Conflict equivalence)】:对于不同事务调度方式 S1 和 S2,如果满足以下两个条件,则事务调度方式 S1 和 S2 是等价的。

  • S1 和 S2 调度方式包括同样的事务集合(每一个事务中的操作的顺序是固定的,不能在不同的调度方式下发生变化);
  • S1 和 S2 调度方式包括同样的冲突操作集合。

【冲突可串行化】:若调度 S 与一个串行调度等价,则称 S 是冲突可串行化。

例如,A = 100,B = 10。

冲突可串行化示例.png

冲突可串行化调度是可串行化调度的充分条件,换言之,若一个调度是冲突可串行化,则一定是可串行化调度。反过来说,可串行化调度不一定是冲突可串行化。例如看下面这个例子:

【示例】:有三个事务 T1 = W1(A)W1(B),T2 = W2(A)W2(B),T3 = W3(B)。调度 L1 = W1(A)W1(B)W2(A)W2(B)W3(B) 串行调度。调度 L2 见下图。

T1          T2          T3
Write(A);
            Write(A);
            Write(B);
Write(B);
                        Write(B);

调度 L2 不满足冲突可串行化,但满足可串行化调度。

【判断方式】:优先图。若存在环则为非冲突可串行化,不存在环则冲突可串行化。

  • 优先图 G = (V, E),V 是顶点集、E 是边集,顶点集由事务组成,边集表示事务的执行的先后顺序。

上个示例的优先图见下图。

冲突可串行化优先图.png

参考

<think>我们正在讨论数据库并发控制中的可串行化调度及其与死锁的关系。根据用户提供的引用,我们可以总结如下关键点:1.**可串行化调度**:一个并发调度是正确的当且仅当它是可串行化的,即其执行结果与某个串行调度的结果相同[^2][^5]。2.**冲突可串行化**:是一种判断可串行化的方法,它要求调度通过交换非冲突操作(不同事务对不同数据的操作,或者同一事务内的操作)能够转换成串行调度,且保持冲突操作的次序不变[^1]。3.**封锁协议**:数据库通过封锁机制(如两段锁协议)来实现可串行化调度。事务在操作数据前加锁,操作后解锁[^3]。4.**死锁**:在并发控制中,多个事务相互等待对方释放锁,导致系统无法推进,这就是死锁。封锁机制虽然保证了可串行化,但可能引入死锁。因此,我们需要解释可串行化调度的概念,以及它在并发控制中如何通过封锁协议实现,同时分析死锁的产生原因和预防方法。回答结构:1.定义可串行化调度(包括冲突可串行化)。2.解释并发控制如何通过封锁(特别是两段锁协议)实现可串行化。3.分析封锁协议与死锁的关系(死锁如何产生)。4.讨论如何防止死锁(破坏死锁条件,如锁超时、顺序加锁等)。注意引用标记:-引用[1]:冲突可串行化、两段锁协议-引用[2]:可串行化调度的定义-引用[3]:封锁的基本概念-引用[5]:可串行化调度的例子同时,我们将生成相关问题。</think>### 可串行化调度与死锁的关系详解 #### 一、可串行化调度的核心概念 1. **基本定义** 可串行化调度指**多个事务的并发执行结果**等价于**按某种顺序的串行执行结果**,这是并发控制中正确性的最高标准[^2][^5]。 $$ \text{并发调度正确} \iff \text{可串行化} $$ 2. **冲突可串行化**(更实用的判定标准) - **冲突操作**:不同事务对同一数据的读写或写写操作(如T1写A时T2读A)[^1] - **判定规则**:若调度通过交换非冲突操作的顺序可转换为串行调度,则为冲突可串行化[^1] - **示例**(引用[5]案例): ```plaintext T1: 读B → A=B+1 → 写A T2: 读A → B=A+1 → 写B 可串行化调度结果:A=3, B=4(与T1→T2或T2→T1串行结果相同) ``` #### 二、并发控制如何实现可串行化 | 方法 | 原理 | 与死锁关联 | |----------------|----------------------------------------------------------------------|-------------------------------| | **封锁协议** | 事务操作数据前加锁(如S锁/X锁),操作后解锁[^3] | 不当加锁顺序→循环等待→死锁[^1] | | **两段锁协议** | 增长阶段(只加锁)→ 收缩阶段(只解锁),**充分保障冲突可串行化**[^1] | 可能引发死锁(如图) | | **时间戳排序** | 按时间戳顺序执行操作 | 避免死锁但可能引发表锁 | ```mermaid graph TD subgraph 两段锁协议下的死锁 T1[事务1] --持有锁A--> T2[等待锁B] T2[事务2] --持有锁B--> T1[等待锁A] end ``` #### 三、死锁产生与可串行化的矛盾 1. **根本冲突** - 封锁协议保证可串行化事务**持续持有锁**(满足持有等待条件) - 可串行化要求**严格调度顺序**(易形成循环等待链) → 两者结合天然存在死锁风险[^3][^5] 2. **典型场景** ```sql -- 事务1 BEGIN TRAN; UPDATE TableA SET ... -- 持有锁A UPDATE TableB SET ... -- 请求锁B(被事务2阻塞) ``` ```sql -- 事务2 BEGIN TRAN; UPDATE TableB SET ... -- 持有锁B UPDATE TableA SET ... -- 请求锁A(被事务1阻塞) ``` → 循环等待导致死锁[^1] #### 四、死锁预防与可串行化保障的平衡 | 方法 | 实现要点 | 对可串行化的影响 | |---------------------|--------------------------------------------------------------------------|-------------------------------| | **锁顺序规则** | 所有事务按固定顺序加锁(如表名排序)[^1] | ✅ 保持可串行化并破坏循环等待 | | **锁超时机制** | 设置锁等待超时(如`SET LOCK_TIMEOUT 5000`) | ⚠️ 可能破坏事务原子性 | | **死锁检测+回滚** | DBMS周期性检测资源分配图,选择牺牲者回滚[^5] | ✅ 回滚后重试保持可串行化 | | **乐观并发控制** | 提交时验证数据版本(如MVCC) | ❌ 仅满足快照隔离级别 | > **关键结论**:两段锁协议可100%保证冲突可串行化,但无法避免死锁;实际系统需结合锁顺序规则+死锁检测实现平衡[^1][^3]。 #### 五、最佳实践建议 1. **事务设计** - 缩短事务时长(减少锁持有时间) - 按固定顺序访问表(破坏循环等待)[^1] ```sql -- 正确示例:统一按TableA→TableB顺序 BEGIN TRAN UPDATE TableA ... UPDATE TableB ... COMMIT ``` 2. **隔离级别选择** - 可串行化隔离级别:最强一致性,高死锁风险 - 读已提交+显式锁:平衡性能与死锁概率 ```sql SELECT * FROM TableA WITH (UPDLOCK) -- 显式加更新锁 ``` 3. **监控工具** ```sql -- SQL Server死锁监控 SELECT * FROM sys.dm_tran_locks -- 查看当前锁[^5] ``` > 最终目标:**在保证可串行化的前提下,通过设计降低死锁概率**,而非牺牲一致性。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值