1131 字6 分钟
二阶段提交 2PL
Databases#数据库 / 事务 / 2PL
什么是 2PL
2PL(Two-Phase Locking),二阶段锁定,是数据库唯一广泛使用的一种可串行化算法。
有时也称为
严格两阶段锁定(SS2PL, strong strict two-phase locking),用来区分其他2PL的变体。
2PL 的特点是允许多个事务同时读取同一个对象,但对象只要有写入(修改或删除),就需要独占访问(exclusive access)的权限:
- 如果事务A读取一个对象,并且事务B想要写入该对象,那么B必须等到A提交或终止才能继续;
- 如果事务A写入一个对象,并且事务B想读取该对象,则B必须等到A提交或终止才能继续;
因此,在2PL中,写入会阻塞其他写入,也会阻塞读,反之,读也会阻塞其他写入。2PL提供了可串行化的性质,可以防止我们之前在数据库事务提到过的所有竞争条件。
2PL用于 MySQL (InnoDB)和 SqlServer 中的可串行化隔离级别。
2PL 的具体实现
读与写的阻塞是通过为数据库中每个对象添加锁来实现的。锁可以处于 共享模式(shared mode) 或 独占模式(exclusive mode) :
- 若事务要读取对象,则须先以共享模式获取锁。允许多个事务同时持有共享锁。但如果有事务已经在该对象上持有排它锁,则这些事务必须等待;
- 若事务要写入一个对象,则必须先以独占模式获取该锁。如果对象上存在其他事务的任何锁,当前事务必须等待;
- 若事务先读取再写入对象,则它需要将其共享锁升级为独占锁,升级锁的过程和获取锁的过程相同;
- 事务的整个执行过程分为两个阶段:扩展阶段获取锁,不允许释放锁。收缩阶段释放所有锁,且无法获取其他锁。这就是两阶段的由来。对于数据库事务,扩展阶段意味着事务从开始到结束为止可以获取锁,而收缩阶段表示提交或终止事务,所有获取到的锁被释放。

谓词锁
谓词锁属于所有符合搜索条件的对象,即使数据库目前还不存在该对象。例如:
select * from bookingswhere room_id = 123 and end_time > '2018-01-01 12:00' and start_time < '2018-01-01 13:00';谓词锁的限制:
- 如果事务A想要读取匹配某些条件的对象,就像这个
select语句查询中那样,它必须获取查询条件上的共享谓词锁(shared-mode predicate lock)。如果另一个事务B持有任何满足这一查询条件对象的排它锁,那么A必须等到B释放它的锁之后才允许进行查询; - 如果事务A想要插入,更新或者删除任何对象,则首先需要检查旧值或新值是否与任何现有的谓词锁匹配。如果事务B持有匹配的谓词锁,那么A必须等到B提交或终止后才能继续。
这里的关键思想是,谓词锁甚至适用于数据库中尚不存在,但将来可能会添加的对象(幻象)。如果两阶段锁定包含谓词锁,则数据库将阻止所有形式的异相,因此其隔离实现了可串行化。
索引范围锁
谓词锁性能不佳:如果活跃事务持有很多锁,检查匹配的锁会非常耗时,大多数数据库在实际中是使用索引范围锁(index-range locking,也称为 next-key locking),这是一个简化的近似版谓词锁,通过锁定比谓词锁更大的范围来近似模拟谓词锁。这种方式也能实现谓词锁的功能,并且开销更低,是一个很好的折衷。
如果没有可以挂载范围锁的索引,数据库可以退化到使用整个表上的共享锁。这是一个安全的回退策略。
参考
本文参考了如下文章: