二阶段提交
2PL(Two-Phase Locking),二阶段锁定,是数据库唯一广泛使用的一种可串行化算法
。
有时也称为
严格两阶段锁定
(SS2PL, strong strict two-phase locking),用来区分其他2PL的变体。
2PL 的特点是允许多个事务同时读取同一个对象,但对象只要有写入(修改或删除),就需要独占访问(exclusive access)的权限:
因此,在2PL中,写入会阻塞其他写入,也会阻塞读,反之,读也会阻塞其他写入。2PL提供了可串行化的性质,可以防止我们之前在[[2023-03-03-database-transactions]]提到过的所有竞争条件。
2PL用于 MySQL (InnoDB)和 SqlServer 中的可串行化隔离级别。
读与写的阻塞是通过为数据库中每个对象添加锁来实现的。锁可以处于 共享模式(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';
谓词锁的限制:
select
语句查询中那样,它必须获取查询条件上的 共享谓词锁(shared-mode predicate lock)
。如果另一个事务B持有任何满足这一查询条件对象的排它锁,那么A必须等到B释放它的锁之后才允许进行查询;这里的关键思想是,谓词锁甚至适用于数据库中尚不存在,但将来可能会添加的对象(幻象)。如果两阶段锁定包含谓词锁,则数据库将阻止所有形式的异相,因此其隔离实现了可串行化。
谓词锁性能不佳:如果活跃事务持有很多锁,检查匹配的锁会非常耗时,大多数数据库在实际中是使用索引范围锁(index-range locking,也称为 next-key locking
),这是一个简化的近似版谓词锁,通过锁定比谓词锁更大的范围来近似模拟谓词锁。这种方式也能实现谓词锁的功能,并且开销更低,是一个很好的折衷。
如果没有可以挂载范围锁的索引,数据库可以退化到使用整个表上的共享锁。这是一个安全的回退策略。
本文参考了如下文章: