Jimmy's blog
返回文章列表
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 bookings
where 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),这是一个简化的近似版谓词锁,通过锁定比谓词锁更大的范围来近似模拟谓词锁。这种方式也能实现谓词锁的功能,并且开销更低,是一个很好的折衷。

如果没有可以挂载范围锁的索引,数据库可以退化到使用整个表上的共享锁。这是一个安全的回退策略。

参考

本文参考了如下文章:

文章信息

许可协议CC BY-NC-SA 4.0