精通Java事务编程(8)-可串行化隔离级别之可串行化的快照隔离

简介: 本系列文章描述了DB并发控制的黯淡:2PL虽保证了串行化,但性能和扩展不好性能良好的弱隔离级别,但易出现各种竞争条件(丢失更新,写倾斜,幻读

本系列文章描述了DB并发控制的黯淡:


2PL虽保证了串行化,但性能和扩展不好

性能良好的弱隔离级别,但易出现各种竞争条件(丢失更新,写倾斜,幻读

串行化的隔离级别和高性能就是相互矛盾的吗?也许不是,一个称为可串行化快照隔离(SSI, serializable snapshot isolation)算法很有前途。提供完整的可串行化保证,而性能与快照隔离相比只有很小性能损失。 SSI在 2008 年首次被提出,如今既用于单节点DB(PostgreSQL9.1后的可串行化)和分布式DB(FoundationDB)。由于 SSI 与其他并发控制机制相比还很年轻,还在实践中证明自己。


3.3.1 悲观锁、乐观锁

两阶段锁是一种 悲观锁机制(pessimistic) ,其设计原则:若有操作可能出错(如与其他事务发生锁冲突),则直接放弃,等待直到绝对安全。和多线程编程中的互斥锁一致。


某种意义上,串行执行是很悲观的:事务期间,每个事务对整个DB(或DB的一个分区)持有互斥锁,我们只能假定每笔事务执行够快、短时持锁,来稍微弥补悲观色彩


相比之下,串行化快照隔离 是一种 乐观锁。如若存在潜在冲突,也不阻止事务,而是继续执行事务,寄希望于一切平安。而当事务想提交时(只有可串行化的事务才被允许提交。),DB会检查是否冲突(即违反隔离性原则):若是,则中止事务并重试。


乐观锁是古老的想法,其优缺点争论已久。若存在很多冲突,则性能不佳,大量事务需中止。若系统已接近最大吞吐量,重试的额外负载会使系统性能更差。


但若系统有足够性能提升空间,且事务之间争用不大,乐观锁比悲观锁更高效。可交换的原子操作能减少争用:如若多个事务同时增加某计数器,则应用增量的顺序(只要计数器不在同一个事务中读取)就无关紧要,所以并发增量可全部应用且无需冲突。


SSI基于快照隔离,即事务中的所有读取都基于DB的一致性快照(参阅本文的快照隔离、可重复读),这和早期乐观锁的主要区别。在快照隔离基础上,SSI新增一种算法检测写入之间的串行化冲突,并确定要中止哪些事务。


3.3.2 基于过期条件来决策

讨论写倾斜时,有一种场景:事务先从DB读一些数据,根据查询结果决定采取后续操作,如修改数据。但快照隔离下,数据可能在查询期间就已被其他事务修改,导致原事务在提交时决策的依据信息已变。


即事务基于某些前提而行动,事务开始时条件成立,如目前有两名医生正在值班,当事务提交时,数据可能已改变,前提已不再成立。


当应用执行查询时(如当前有多少医生在值班),DB本身不知道应用会如何使用该查询结果。为了安全,DB假定对该结果集的变更都可能会使该事务中的写无效。 即事务中的查询与写可能存在因果依赖关系。为提供可串行化隔离,DB必须检测事务是否会修改其它事务的查询结果,并在此情况下中止写事务。


DB如何知道查询结果是否已变?可分为如下case:


读取是否作用于一个(即将)过期的MVCC对象(读取之前已经有未提交的写入)

检查写是否影响即将完成的读取(读取后,又有新写入)

3.3.3 检测旧MVCC读取

快照隔离通常采用MVCC实现。当事务从 MVCC DB的一致性快照读时,会忽略创建快照时还没提交的事务写入。如图-10:


事务43认为 Aliceon_call = true ,因事务 42(修改 Alice 值班状态)还没提交

然而,事务43提交时,事务42已提交

即从快照读取时,被忽略的写已生效,直接导致事务43做决定的前提不再成立。

7.png



为防止这种异常,DB需跟踪一个事务由于MVCC可见性规则而被忽略的其它事务写。当事务提交时,DB会检查是否存在被忽略的写现在已被提交的,若是,则当前事务必须中止。


为何要等到提交?当检测到读旧值,为何不立即中止事务43,考虑如下场景:


若事务43是只读事务,则无需中止,因为无写倾斜风险


当事务43读DB 时,DB还不知道事务是否要稍后执行写操作


此外,事务42可能在事务43提交时,被中止或仍处于未被提交,因此读取的并非旧值


通过避免不必要的中止,SSI可高效支持那些需在一致性快照中运行很长时间的读事务。

3.3.4 检测写是否影响之前的读

读取数据后,另一个事务修改了数据:

6.png



2PL下讨论了索引范围锁,允许DB锁定和某查询匹配的所有行,如WHERE shift_id = 1234。可在此使用类似技术,只有一点差异:SSI锁不阻塞其他事务。


图-11中,事务42、43 都在班次1234查找值班医生。若 shift_id 有索引,则DB能使用索引项1234记录事务42、43读取这个数据的事实。若无索引,可在表级别跟踪此信息。该信息只需保留很小一段时间:当所有并发事务完成后,就能丢弃。


当另一事务写时,先检查索引,从而确定是否在最近存在一些读目标数据的其它事务。这过程类似在受影响字段范围上获取写锁,但锁不会阻塞其它事务读取,而是直到读事务提交时才进一步通知它们:所读到的数据已变化。


图-11中,事务43通知事务42其先前读已过时,反之亦然。事务42先提交并成功,尽管事务 43写影响了42 ,但因43没提交,所以写还没生效。当43提交时,来自42的冲突写入已被提交,所以43必须中止。


3.3.5 性能

许多工程细节会影响算法实际效果。如一个需权衡考虑的是跟踪事务的读、写的粒度:


若DB详细跟踪每个事务的操作(细粒度),确实能准确确定哪些事务需中止,但记录元数据的开销可能也很大

而跟踪速度更快时(粗粒度),可能导致更多不必要的事务中止

有的case读过期数据不会造成太大影响:这还是完全取决于具体场景,有时可确信执行结果都是可串行化的,PostgreSQL 使用该理论减少不必要的中止。


相比于2PL,可串行化快照隔离最大优点:事务无需阻塞等待其它事务所持有的锁。这和快照隔离一样,读写不互相阻塞。这使查询延迟更稳定、可预测。尤其是只读查询可运行在一致快照,无需任何锁,对读密集系统友好。


相比于串行执行,可串行化快照隔可突破单CPU核吞吐量限制:FoundationDB将检测到的串行化冲突分布在多台机器,从而提高吞吐量。即使数据可能跨多台机器分区,事务也能在保证可串行化隔离等级同时,读写多个分区中的数据。


事务中止率会显著影响SSI性能。如长时间读、写数据的事务很可能会发生冲突并中止,因此SSI要求读写型事务尽量短(但只读的长事务则没问题)。总体上,对慢事务,SSI比2PL或串行执行更能容忍。

目录
相关文章
|
1天前
|
存储 安全 Java
Java并发编程中的高效数据结构:ConcurrentHashMap解析
【4月更文挑战第25天】在多线程环境下,高效的数据访问和管理是至关重要的。Java提供了多种并发集合来处理这种情境,其中ConcurrentHashMap是最广泛使用的一个。本文将深入分析ConcurrentHashMap的内部工作原理、性能特点以及它如何在保证线程安全的同时提供高并发性,最后将展示其在实际开发中的应用示例。
|
2天前
|
Java API 调度
[Java并发基础]多进程编程
[Java并发基础]多进程编程
|
2天前
|
Java API 调度
[AIGC] 深入理解Java并发编程:从入门到进阶
[AIGC] 深入理解Java并发编程:从入门到进阶
|
2天前
|
前端开发 Java 测试技术
Java从入门到精通:4.1.1参与实际项目,锻炼编程与问题解决能力
Java从入门到精通:4.1.1参与实际项目,锻炼编程与问题解决能力
|
3月前
|
Oracle Java 关系型数据库
Java 编程指南:入门,语法与学习方法
Java 是一种流行的编程语言,诞生于 1995 年。由 Oracle 公司拥有,运行在超过 30 亿台设备上。Java 可以用于: 移动应用程序(尤其是 Android 应用) 桌面应用程序 网络应用程序 网络服务器和应用程序服务器 游戏 数据库连接 等等!
37 1
|
8月前
|
存储 算法 Java
吐血整理Java编程基础入门技术教程,免费送
吐血整理Java编程基础入门技术教程,免费送
33 0
|
开发框架 Java C语言
Java学习路线-1:编程入门
Java学习路线-1:编程入门
71 0
|
小程序 安全 前端开发
【Java编程进阶】Java语言基础入门篇
整个Java全栈编程知识体系十分庞大,包括JavaSE知识,Web前端,Web后端,数据库相关的知识等,初学者应该系统踏实的学习,一步一个脚印。Java语言是一种完全面向对象的跨平台语言。有很多突出的优点,例如简单易学,面向对象,分布式,安全可靠,解释型语言,跨平台运行,可移植高性能多线程,可实现网络编程等。
141 0
【Java编程进阶】Java语言基础入门篇
|
Java
真的,Java并发编程基础入门看这个就够了
Java并发编程学习之02Java并发编程入门指南 1. Java天生多线程 2. Java启动多线程实现方式 2.1 实现代码 2.2 Thread和Runnable的区别 2.3 start和run方法的区别 3. Java如何停止线程呢 3.1 已弃用方法 3.2 推荐使用 4. 守护线程 5. 优先级 6. 线程生命周期 代码仓
133 0
真的,Java并发编程基础入门看这个就够了
|
消息中间件 Oracle 架构师
花2万块买的教程!java编程入门到精通
花2万块买的教程!java编程入门到精通
花2万块买的教程!java编程入门到精通