DBMS - 冲突可串行化
在处理数据库事务时,我们经常会连续考虑多个事务。当多个事务同时运行时,可能会发生冲突,导致结果不正确。为此,我们需要对调度进行串行化。串行化有多种类型,其中冲突可串行化就是其中之一。它确保事务得到正确处理,而不会引起不一致性。请阅读本章,详细了解冲突可串行化的概念。
DBMS 中的调度
要理解冲突可串行化的思想,首先必须了解什么是调度。在 DBMS 中,调度是事务操作(如读或写)的序列。这些操作按照特定顺序排列。它决定了事务的执行方式,特别是当多个事务并发运行时。
串行调度
在讨论调度时,我们必须考虑串行调度和非串行调度。串行调度按顺序处理事务,不允许重叠。例如:事务 T1 完全运行完毕后,事务 T2 才开始。
这种调度简单明了,并且总是保持一致性,因为事务之间是隔离的。但是,由于缺乏并发性,它可能较慢。以下是一个串行调度的示例 −
| Schedule 1: T1 | Operation | Schedule 2: T2 | Operation |
|---|---|---|---|
| T2: Read(B) | Read B | ||
| T2: Write(B) | Write B | ||
| T2: Read(A) | Read A | ||
| T2: Write(A) | Write A | ||
| T1: Read(A) | Read A | ||
| T1: Write(A) | Write A | ||
| T1: Read(B) | Read B | ||
| T1: Write(B) | Write B |
在这个示例中,T2 在 T1 开始之前完成其所有操作。由于事务不重叠,该调度保证是一致的。
非串行调度
另一方面,非串行调度允许多个事务重叠。这意味着来自不同事务的一些操作可能同时运行。这通过减少等待时间来提高效率。但如果不仔细管理,可能会导致不一致性。以下是一个非串行调度的示例 −
| Schedule 1: T1 | Operation | Schedule 2: T2 | Operation |
|---|---|---|---|
| T1: Read(A) | Read A | ||
| T1: Write(A) | Write A | ||
| T2: Read(B) | Read B | ||
| T2: Write(B) | Write B | ||
| T1: Read(B) | Read B | ||
| T1: Write(B) | Write B | ||
| T2: Read(A) | Read A | ||
| T2: Write(A) | Write A |
在这里,事务 T1 和 T2 并发运行。虽然这提高了性能,但无法立即保证一致性。
冲突可串行化的重要性
虽然串行调度总是保持一致性,但效率低下。另一方面,非串行调度允许更好的并发性,但如果事务相互干扰,可能会引起问题。
冲突可串行化有助于判断非串行调度是否可以通过交换非冲突指令重新排列成串行调度。如果可以转换为串行调度,则系统可以在允许并行执行的同时保证一致性。
冲突指令
要判断非串行调度是否具有冲突可串行化,我们需要识别两个指令何时冲突。
什么使指令冲突?
- 它们属于不同的 transaction。
- 它们操作同一个数据项。
- 其中至少有一个是写操作。
如果满足所有三个条件,则这些指令被认为是冲突的。这意味着交换它们会改变最终结果。
冲突和非冲突指令的示例
让我们通过一些示例来更好地理解这一点 −
示例 1:非冲突指令
由于这些指令操作不同的数据项(A 和 B),它们是非冲突的。我们可以交换它们而不改变结果。
- T1: Read(A)
- T2: Write(B)
示例 2:冲突指令
在这里,两个指令都操作相同的数据项 A。其中一个是写操作。这使得它们冲突,即不能交换。
- T1: Read(A)
- T2: Write(A)
示例 3:写-写冲突
尽管两个指令都是写操作,但 A 的最终值取决于哪个操作最后执行。这会产生冲突。因此交换它们会改变结果。
- T1: Write(A)
- T2: Write(A)
冲突的现实生活类比
想象两个厨师在厨房工作 −
- 厨师 1 准备沙拉。
- 厨师 2 烤蛋糕。
由于他们操作不同的菜肴,他们可以同时工作。但如果两个厨师同时试图使用烤箱,就会产生冲突,因为一次只能有一人使用烤箱。同样,在 DBMS 中,当事务竞争相同数据项时,就会发生冲突。
检查冲突可串行化
让我们通过一个实际示例更详细地了解如何检查一个调度是否是冲突可串行化的。
示例调度
| T1 | T2 |
|---|---|
| Read(A) | |
| Write(A) | |
| Read(B) | |
| Write(B) |
步骤 1:识别事务和数据项
- T1 操作 A。
- T2 操作 B。
由于 A 和 B 是不同的数据项,T1 和 T2 的指令是非冲突的。
步骤 2:尝试重新排列指令
我们可以将调度重新排列如下 −
| T1 | T2 |
|---|---|
| Read(B) | |
| Write(B) | |
| Read(A) | |
| Write(A) |
这个顺序对应一个串行调度,其中 T2 在 T1 开始之前完成。
由于我们可以通过交换非冲突指令将原始非串行调度重新排列为串行调度,因此原始调度是冲突可串行化的。
注意事项:冲突可串行化
虽然冲突可串行化很有用,但它并不是确保一致性的唯一方法。有些调度即使不是冲突可串行化的,也可能是 consist 的。但是,冲突可串行化提供了一种清晰且直接的方法来验证一致性。
许多人认为,如果一个调度不是冲突可串行化的,它就不一致。这并不总是正确的。冲突可串行化测试是一种过滤器。它有助于识别一致的调度,但测试失败并不保证不一致。
结论
在本章中,我们介绍了 DBMS 中的冲突可串行化概念,以及它如何帮助维护数据库一致性。我们理解了调度、调度类型以及何时指令被视为冲突。我们学习了如何检查非串行调度是否可以通过交换非冲突指令转换为串行调度。