基于锁的协议2024年12月27日 | 8 分钟阅读 在这种协议中,任何事务都不能读取或写入数据,除非它获得了该数据的适当锁。锁有两种类型: 什么是锁? 它与每个数据项相关联,代表该数据项相对于可能对其执行的操作的状态。它的值用于锁定方案中,以操作相关联的数据项并控制并发访问。锁定事务正在使用的数据项可以防止同时运行的其他事务使用这些锁定的数据项。这是确保可串行化最常用的技术之一,锁值的操作称为锁定。 锁的类型 各种类型的锁用于控制并发。根据锁的类型,锁管理器授予或拒绝其他操作对同一数据项的访问。首先讨论二进制锁,它们简单但实际用途较少。然后我们将讨论共享锁和排他锁,也称为读/写锁,它们具有更多的锁定能力和广泛的实际用途。 二进制锁 二进制锁只能有两个值: 为简单起见,它们用 1 或 0 表示。每个数据项都有一个与之关联的单独锁。如果数据项被锁定,则请求该数据项的数据库操作无法访问它;如果数据项未锁定,则可以在请求时访问它。 数据项 A 的二进制锁定关联以下两个操作。 事务首先使用 Lock() 操作锁定数据项来请求访问数据项。在此过程中,如果另一个并发事务的另一个操作试图访问同一数据项,则它将被强制等待,直到锁定数据项的事务使用 Unlock() 操作解锁同一数据项。当给定事务锁定了数据项并完成了对该数据项的所有操作后,它会自动解锁该数据项,以便其他事务可以使用它。 使用二进制锁定方案时必须遵循以下规则: - Lock (): 此操作必须在事务执行任何更新操作(例如读取或写入)之前由事务发出。
- Unlock (): 此操作必须在事务中所有读取或写入操作完成后由事务发出。
- 如果事务已经持有数据项上的锁,则不能释放 lock() 操作。
- 除非事务已经持有数据项上的锁,否则不能发出 Unlock() 操作。
考虑两个事务 T1 和 T2,它们分别将账户余额更新为 200 卢比和 300 卢比,如果这些事务同时运行,则可能的调度如下所示。 T1 | T2 | 调查表 |
---|
读取 (A) | 读取 (A) | T1: 读取 (A) | A: = A + 200 | A: = A + 300 | T2: 读取 (A) | 写入 (A) | 写入 (A) | T1: A: = A + 200 | | | T1: 写入 (A) | | | T2: A: = A + 300 | | | T2: 写入 (A) |
因此,在此调度中会发生丢失更新问题。现在,如果我们对事务应用二进制锁,则会发生以下情况。 T1 | T2 | 调查表 |
---|
Lock (A) | Lock (A) | T1: 锁定 (A) | 读取 (A) | 读取 (A) | T1: 读取 (A) | A: = A + 200 | A: = A + 300 | T1: A: = A + 200 | 写入 (A) | 写入 (A) | T1: 写入 (A) | Unlock (A) | Unlock (A) | T1: 解锁 (A) | | | T2: 锁定 (A) | | | T2: 读取 (A) | | | T2: A: = A + 300 | | | T2: 写入 (A) | | | T2: 解锁 (A) |
因此很明显,由事务 T1 和 T2 组成的调度将是可串行化的。这是因为如果 T1 在 T2 之前锁定账户 A,那么 T2 将无法继续,直到事务 T1 解锁账户 A。 同样,如果 T2 在 T1 之前锁定账户 A,那么 T1 将无法继续,直到事务 T2 解锁账户 A。 在二进制锁定方法中,最多只有一个事务可以持有特定数据项上的锁。因此,没有两个事务可以访问同一数据项。 锁有两种类型: 1. 共享锁 - 它也称为只读锁。在共享锁中,数据项只能由事务读取。
- 它可以在事务之间共享,因为当事务持有锁时,它不能更新数据项上的数据。
2. 排他锁 - 在排他锁中,数据项可以由事务读取和写入。
- 此锁是排他性的,在此锁中,多个事务不能同时修改相同的数据。
使用共享/排他锁时必须遵循以下规则: - 在事务执行读取操作之前,必须对事务中的数据项应用共享/排他锁。
- 在事务中对数据项进行任何写入操作完成之前,必须对事务中的数据项应用排他锁。
- 事务必须在对数据项的所有读取/写入操作完成后,对事务中的数据项发出解锁操作。
- 除非事务已经持有数据项上的共享/排他锁,否则事务不得执行解锁操作。
- 已经持有数据项上的共享/排他锁的事务不能对同一数据项发出共享锁。同样,已经持有数据项上的共享/排他锁的事务不能对同一数据项发出排他锁。
共享、排他锁和解锁之间的兼容关系由下表给出。在此矩阵中,我们假设锁定请求是由尚未持有数据项锁的事务发出的。 | 共享 | 排他 | 解锁 |
---|
共享 | 是的 | 不能 | 是的 | 排他 | 不能 | 不能 | 是的 | 解锁 | 是的 | 是的 | -- |
上述矩阵可以解释如下: - 如果数据项上的当前锁定状态是共享的,例如事务已经对数据项 X 应用了共享锁,那么另一个事务也可以对同一数据项 X 应用共享锁,即请求的锁模式可以是共享的。但它不能对数据项 X 应用排他锁,因为数据项 X 已经被其他事务共享锁定了。但是,您可以解锁被某些事务共享锁定的数据项 X。
- 如果数据项上的当前锁定状态是排他性的,即事务已经对数据项 X 发出了排他锁,则其他事务不能对同一数据项 X 发出共享或排他锁。但是,我们可以解锁被同一事务排他锁定的数据项 X。
- 如果数据项 X 未锁定,即处于解锁状态,则可以使用共享/排他锁进行锁定。但是,解锁的数据项不能再次解锁。
现在考虑存在两个银行账户 A 和 B 的情况,它们的余额分别为 1000 卢比和 900 卢比。让我们创建两个事务来创建账户余额,另一个事务将 200 卢比从账户 A 转账到账户 B。 T1 | T2 |
---|
锁定 X(A) | 锁定 X(总和) | 读取 (A) | 总和: = 0 | A: = A - 200 | 锁定 S(A) | 写入 (A) | 读取 (A) | Unlock (A) | 总和: = 总和 + A | 锁定 X(B) | Unlock (A) | 读取 (B) | 锁定 S (B) | B: = B + 200 | 读取 (B) | 写入 (B) | 总和: = 总和 + B | 解锁 (B) | 写入 (总和) | | 解锁 (B) | | 解锁 (总和) |
如果上述两个事务 T1 和 T2 以串行顺序执行,例如 T1 然后 T2,或 T2 然后 T1,则事务 T2 每次都将显示 1900 卢比的值。此处,Lock X 表示排他锁,Lock S 表示共享锁。如果 T1 和 T2 事务同时执行,则调度如下所示。 调查表 |
---|
T2: 锁定 X(总和) | T2: 总和: = 0 | T2: 锁定 S(A) | T2: 读取 (A) | T2: 总和: = 总和 + A | T2: 解锁 (A) | T1: 锁定 X(A) | T1: 读取 (A) | T1: A: = A - 200 | T1: 写入 (A) | T1: 解锁 (A) | T1: 锁定 X(B) | T1: 读取 (B) | T1: B: = B + 200 | T1: 写入 (B) | T1: 解锁 (B) | T2: 锁定 S (B) | T2: 读取 (B) | T2: 总和: = 总和 + B | T2: 写入 (总和) | T2: 解锁 (B) | T2: 解锁 (总和) |
在上述调度中,账户 A 和 B 的余额总和为 2100 卢比,这是不正确的。此锁定方案未能解决不一致读取问题,因为在这些事务同时执行后,数据库创建了一个不一致的状态,因为在对账户 A 的余额进行修改之前,账户 A 的值已添加到“总和”中。 此调度是一个不可串行化调度,即使两个事务都锁定了和解锁了它们使用的数据项,也发生了不可串行化调度。因此,锁定可以帮助实现可串行化,但不能保证它。 有四种类型的锁协议可用:1. 简单锁协议这是事务期间锁定数据的最简单方式。简单锁协议允许所有事务在插入、删除或更新数据之前获取数据上的锁。它将在事务完成后解锁数据项。 2. 预先声明锁协议- 预先声明锁协议评估事务以列出它们需要锁定的所有数据项。
- 在启动事务执行之前,它会向 DBMS 请求所有这些数据项上的所有锁。
- 如果所有锁都已授予,则此协议允许事务开始。当事务完成时,它会释放所有锁。
- 如果所有锁都未授予,则此协议允许事务回滚并等待,直到所有锁都已授予。

3. 两阶段锁定 (2PL)- 两阶段锁定协议将事务的执行阶段分为三个部分。
- 在第一部分中,当事务执行开始时,它会寻求所需锁的许可。
- 在第二部分中,事务获取所有锁。第三阶段在事务释放其第一个锁后立即开始。
- 在第三阶段中,事务不能请求任何新锁。它只释放已获得的锁。

2PL 有两个阶段: 增长阶段: 在增长阶段,事务可以获取数据项上的新锁,但不能释放任何锁。 收缩阶段: 在收缩阶段,事务持有的现有锁可以释放,但不能获取任何新锁。 在下面的示例中,如果允许锁转换,则会发生以下阶段: - 锁的升级(从 S(a) 到 X (a))在增长阶段允许。
- 锁的降级(从 X(a) 到 S(a))必须在收缩阶段完成。
示例

以下方式显示了解锁和锁定如何与 2-PL 配合使用。 事务 T1 - 增长阶段: 从步骤 1-3
- 收缩阶段: 从步骤 5-7
- 锁点: 在 3
事务 T2 - 增长阶段: 从步骤 2-6
- 收缩阶段: 从步骤 8-9
- 锁点: 在 6
4. 严格两阶段锁定 (Strict-2PL)- Strict-2PL 的第一阶段与 2PL 类似。在第一阶段中,在获取所有锁之后,事务继续正常执行。
- 2PL 和严格 2PL 之间唯一的区别是 Strict-2PL 在使用锁后不会释放锁。
- Strict-2PL 会等待整个事务提交,然后一次性释放所有锁。
- Strict-2PL 协议没有锁释放的收缩阶段。

它不像 2PL 那样有级联中止。
|