使用 DBMS 中的锁进行并发控制
锁是维护 DBMS 中并发控制的不可或缺的一部分。任何实现基于锁的并发控制的系统中,事务在获得所需锁之前都无法读取或写入语句。
基于锁的协议中有两种类型的锁。它们是:
- 二进制锁 - 这些只能处于锁定或解锁两种状态之一。
- 共享/排他锁 - 当仅执行读取操作时获取共享锁。由于没有数据被更改,因此可以在多个事务之间共享共享锁。执行写入操作时使用排他锁。只有持有独占锁的事务才被允许更改数据值。
不同的锁定协议有 −
简单锁定协议
在执行写入操作之前,事务会获得对数据值的锁定。写入操作之后,可以释放锁定。简单锁定协议的一个例子是:
T1 | T2 |
---|---|
R(A) | |
R(A) | |
Lock(B) | |
R(B) | |
W(B) | |
Unlock(B) | |
Lock(C) | |
R(C) | |
W(C) | |
Unlock(C) | |
Commit | |
Commit |
上面有两个事务T1和T2。读操作不需要锁,但在写操作之前,这两个事务各自获取一个锁,之后释放它。
两阶段锁定协议
两阶段锁定协议有两个阶段,即增长阶段和收缩阶段。事务只能在增长阶段获取锁。当它进入收缩阶段时,它可以释放之前获取的锁,但不能获取新的锁。排他锁用X表示,共享锁用S表示。两阶段锁定协议的一个例子是 −
T1 | T2 |
---|---|
S(A) | |
R(A) | |
S(A) | |
R(A) | |
X(B) | |
R(B) | |
W(B) | |
X(C) | |
R(C) | |
W(C) | |
Unlock(C) | |
Unlock(A) | |
Unlock(B) | |
Unlock(A) | |
Commit | |
Commit |
在上面的例子中,T1 和 T2 使用共享锁共享变量 A,因为在 A 上只执行读取操作。T1 获取 B 上的独占锁以执行写入操作,并在不久后释放它。T2 对 C 执行相同操作。
严格两相锁定协议
严格两相锁定协议类似于两相锁定协议。唯一的区别是,在严格 2PL 协议中,协议获取的所有独占锁都需要保持,直到协议提交或中止。严格两相锁定协议的一个例子是:
T1 | T2 |
---|---|
S(A) | |
R(A) | |
S(A) | |
R(A) | |
X(B) | |
R(B) | |
W(B) | |
X(C) | |
R(C) | |
W(C) | |
Unlock(A) | |
Unlock(A) | |
Commit | |
Unlock(B) | |
Commit | |
Unlock(C) |
在上面的例子中,T1 和 T2 使用共享锁共享变量 A,因为在 A 上只执行读取操作。T1 获取 B 上的排他锁以执行写入操作,T2 获取 C 上的排他锁。只有在事务提交后才会释放排他锁。但是,共享锁没有这样的限制。
严格两相锁定协议
严格两相锁定协议仅仅是两相锁定协议和严格两相锁定协议的扩展。在这里,事务持有的所有锁,无论是共享锁还是排他锁,只有在事务提交或中止后才会释放。严格两相锁定协议的一个例子是:
T1 | T2 |
---|---|
S(A) | |
R(A) | |
S(A) | |
R(A) | |
X(B) | |
R(B) | |
W(B) | |
X(C) | |
R(C) | |
W(C) | |
Commit | |
Unlock(A) | |
Unlock(B) | |
Commit | |
Unlock(A) | |
Unlock(C) |
在上面的例子中,T1 和 T2 使用共享锁共享变量 A,因为在 A 上只执行读操作。T1 获取了 B 上的排他锁以进行写操作,而 T2 获取了 C 上的排他锁。共享锁和排他锁都仅在事务提交后才被释放。