近30年,DB只有一种广泛使用的串行化算法:两阶段加锁 1
请注意,虽然两阶段锁定(2PL)听起来非常类似于两阶段提交(2PC),但是完全不同概念
之前我们知道,加锁可防止脏写:即若两个事务同时尝试写入同一对象,则锁可确保第二个写必须等第一个写完成事务(中止或提交)才能继续。
两阶段锁定类似,但锁的强制性更高。只要没有写入,就允许多个事务同时读取同一个对象。但对象只要有写,就得加锁独占访问:
2PL不仅在并发写互斥,读写之间也互斥。快照级别隔离是读写不互斥,这是 2PL 和快照隔离的关键区别。且因 2PL 提供串行化,可防止前文讨论的所有竞争条件,包括丢失更新和写倾斜。
2PL已在:
读与写的阻塞是通过为数据库中每个对象添加锁来实现的。锁可处于 共享模式或独占模式,使用如下:
由于使用了这么多锁,很容易死锁:如事务A等待B释放锁,而B等A释放锁。DB会自动检测事务之间死锁,并强行中止一个。被中止的事务需由应用层重试。
其巨大缺点及1970s以来没有被广泛使用的原因还是其性能:事务吞吐量和查询响应时间比弱隔离级别下差太多。
部分因为获取、释放锁开销,但更重要是并发性降低。按设计,若两个并发事务试图做任何可能导致竞争条件的事情,则其一必须等待另一完成。
传统关系DB不限制事务的执行时间,因为它们是为等待人类输入的交互式应用而设计。结合2PL,最终结果是,当一个事务还需等待另一事务时,则最终等待时间几乎无上限。即使能保证所有事务都很短,若有多事务同时访问同一对象,会形成一个等待队列,事务需要等待前面事务完成后才能继续。
因此,2PL DB的访问延迟具有极大不确定性,若负载存在严重竞争,以百分比方式观察延迟指标会发现很缓慢。可能某缓慢事务或一个访问大量数据并获取许多锁的事务,就能把系统其他部分拖慢。当需要稳定操作时,这种不稳定性是致命的。
基于锁实现的RX也可能死锁,但 2PL 下取决于事务的访问模式,死锁更频繁。这可能是一个额外的性能问题:当事务由于死锁而被中止并被重试时,应用层就需从头重试。若死锁频繁,则最后性能和效率必然大打折扣。
对加锁,忽略了一个微妙但重要的细节。在写倾斜幻读中的幻读问题,即一个事务改变另一个事务的查询结果。可串行化隔离也必须防止幻读。
会议室预订案例,若事务在查询某时间段内一个房间的预订情况,则另一个事务不能同时插入或更新同一时间段内该房间的预订 (可同时插入其他房间的预订或在不影响另一个预定的条件下预定同一房间的其他时间段)。
要实现就需要谓词锁(predicate lock),类似共享/独占锁,但不属于特定对象(如表的某行),而是作用于所有符合某些搜索条件的对象,如:
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释放锁后才能继续执行查询关键在于,谓词锁甚至适用于数据库中尚不存在,但将来可能会添加的对象(幻象)。如果两阶段锁定包含谓词锁,则数据库将阻止所有形式的写入偏差和其他竞争条件,因此其隔离实现了可串行化。
但谓词锁性能不佳:若活跃事务持有很多锁,则检查匹配的锁很耗时。因此,大多2PL DB实际上实现的是索引范围锁(index-range locking,也称为 next-key locking),本质是对谓词锁的简化或近似。
简化谓词锁的方式是扩大其保护的对象,这肯定是安全的。如若你有12:00~13:00预订 123 号房间的谓词锁,则锁定123号房间的所有时间段或锁定12:00~13:00时间段的所有房间就是安全的近似。这样,任何与原始谓词锁冲突的操作肯定也和近似后的区间锁相冲突。
房间预订DB,一般在:
room_id
列建索引start_time
和 end_time
上有索引否则前面的查询在大型DB上的速度会很慢。
room_id
上,并且数据库使用此索引查找 123 号房间的现有预订。现在数据库可以简单地将共享锁附加到这个索引项上,指示事务已搜索 123 号房间用于预订。无论哪种,查询条件的近似值都附加到某个索引上。若另一事务想插入、更新或删除同一房间和/或重叠时间段的预订,则须更新这些索引的相同部分,就一定会和共享锁冲突,将被迫等到共享锁被释放。
这有效防止了幻读和写倾斜。索引范围锁并不像谓词锁精确(会锁定更大范围的对象,超出维持可串行化所必需的范围),但由于开销低得多,是很好的折衷方案。
若无可挂载范围锁的索引,则DB可退化到使用整表的共享锁。这对性能不利,会阻止所有其他事务的写,但这是一个安全的回退位置。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://javaedge.blog.csdn.net/article/details/125962260
内容来源于网络,如有侵权,请联系作者删除!