事务要解决的问题
事务存在的意义是为了保证系统中数据的一致性:所有数据符合期望,相互关联的数据之间不会产生矛盾;
一致性需要AID 来共同努力
- 原子性(Atomic):多个数据要么同时全部成功,要么同时全部失败;
- 隔离性(Isolation):保证各业务正在读写的数据相互独立,不会批次影响;
- 持久性(Durability):保证所有成功被提交的数据修改,都能正确地被持久化,不丢失数据;
本地事务
实现原子性和持久性
原子性即是保证事务要么提交,要么回滚;
持久性是保证事务提交后如果崩溃要恢复或者擦除;
- 使用Commit Logging 的实现方式(提交日志)
日志一旦成功写入了Commit Record,事务就是成功的,此时如果发生崩溃,按照 Commit Log 将数据重写即可;如果日志没有写入Commit Record 就崩溃,那事务都没有提交,直接标记这部分提交日志为回滚状态即可;
Commit Logging 成立的前提是决不允许事务提交前就修改磁盘上的数据; - 使用 Write-Ahead Logging 实现方式(提前写入日志),来源于ARIES 理论
事务提交后,先记录 Undo Log ,用于事务回滚时对数据变动擦除;与 Commit Log 类似的日志这里被命名为 Redo Log,用于崩溃恢复时重演数据变动;
Write-Ahead Logging 在崩溃恢复时会经历3个阶段:
1. 分析阶段,从最后一次检查点扫描日志,找出没有End Record 日志的事务,组成待恢复的事务集合;
1. 重做阶段:找出有Commit Record的日志(事务已提交),将日志修改的数据写入磁盘,写完后追加End Record 日志,移除待恢复事务集合;
1. 回滚阶段:此时剩余待恢复事务集合内都是需要回滚的食物,根据 Undo Log 将已经写入磁盘的数据重新改写回去;
Write-Ahead Logging 将何时写入变动数据分为两类情况:
- FORCE:当事务提交后,要求变动数据必须同时完成写入则称为 FORCE,如果不强制变动数据必须同时完成写入则称为 NO-FORCE。现实中绝大多数数据库采用的都是 NO-FORCE 策略,只要有了日志,变动数据随时可以持久化,从优化磁盘 I/O 性能考虑,没有必要强制数据写入立即进行。
- STEAL:在事务提交前,允许变动数据提前写入则称为 STEAL,不允许则称为 NO-STEAL。从优化磁盘 I/O 性能考虑,允许数据提前写入,有利于利用空闲 I/O 资源,也有利于节省数据库缓存区的内存。
实现隔离性
锁种类
待进一步了解MySQL锁
- 行锁:S,X,G,L,NK(G+L),插入意图锁(Insert Intention Lock)
- 表锁:S,X,IS,IX
隔离级别
以下依次隔离性降低;
待进一步了解MySQL隔离级别实现;
RR:所有数据加S、X,持续至事务结束;
RC:对涉及的数据加X、S,X持续到事务结束,S读完马上释放;
RU:涉及的数据加X,完全不加S
串行化S:所有读写数据都加读锁、写锁、范围锁
MVCC
全局事务
XA / 2PC
XA 是由 X/Open 组织提出的分布式事务规范,XA 规范主要定义了事务协调者(Transaction Manager)和资源管理器(Resource Manager)之间的接口。
在XA规范之前,存在着一个DTP模型,该模型规范了分布式事务的模型设计。
DTP 模型
DTP 模型 定义了5个基础功能组件,分别是:
AP (Application Program): 定义事务边界,指定组成事务的行为
RM (Resource Manager): 数据库、文件系统等资源
TM (Transaction Manager): 给事务制定标识符,监控整个过程,负责完成事务和失败后恢复。
CRMs (Communication Resource Managers): 控制一个或多个 TM domain 之间分布式应用的通信。
A communication protocol :定义了 CRMs 支持的、分布式应用使用的底层通信服务。
XA协议
XA {START|BEGIN} xid [JOIN|RESUME] //开启XA事务,如果使用的是XA START而不是XA BEGIN,那么不支持[JOIN|RESUME],xid是一个唯一值,表示事务分支标识符
XA END xid [SUSPEND [FOR MIGRATE]] //结束一个XA事务,不支持[SUSPEND [FOR MIGRATE]]
XA PREPARE xid 准备提交
XA COMMIT xid [ONE PHASE] //提交,如果使用了ONE PHASE,则表示使用一阶段提交。两阶段提交协议中,如果只有一个RM参与,那么可以优化为一阶段提交
XA ROLLBACK xid //回滚
XA RECOVER [CONVERT XID] //列出所有处于PREPARE阶段的XA事务
XA规范定义了一个xid有4个部分组成
gtrid:全局事务标识符(global transaction identifier),最大不能超过64字节
bqual:分支限定符(branch qualifier),最大不能超过64字节
-
data:xid的值,其是 gtrid和bqual拼接后的内容。因为gtrid和bqual最大都是64个字节,因此data的最大长度为128。不过,在xid的结构体中,并没有gtrid和bqual,只有gtrid_length、bqual_length。由于二者的内容都存储在data中,因此我们可以根据data反推出gtrid和bqual。举例来说,假设gtrid为”g12345”(5个字节),bqual为”b456”(4个字节)。那么在构造xid结构体时,gtrid_length=5,bqual_length=4,data=”g12345b456”,那么在反推的时候:
从data[0]到data[gtrid_length-1]之间的部分就是gtrid的值;从data[gtrid_length]到data[gtrid_length+bqual_length-1]部分就是bqual的值。
formatId:而formatId的作用就是记录gtrid、bqual的格式,类似于memcached中flags字段的作用。XA规范中通过一个结构体约定了xid的组成部分,但是并没有规定data中存储的gtrid、bqual内容到底应该是什么格式。你可以选择使用数字,也可以选择使用字符串,到底选择什么由开发者自行决定,只要最终能保证data中的内容是全局唯一的即可。XA规范建议使用OSI CCR风格来组织xid的内容,此时formatId应该设置为0.
XA状态流转
通过jdbc操作mysql xa事务
示例可参看 atomikos 笔记
JTA
JTA,即Java Transaction API,JTA允许应用程序执行分布式事务处理——在两个或多个网络计算机资源上访问并且更新数据。
因为主流数据库都实现了XA协议,JTA应该可以理解为在XA基础上定义的API。
-
JTA规范类图参考
atomikos学习参看 笔记
对2PC的另一个开源实现是seata的XA模式
粗看未实现JTA,待了解原理和实现
3PC
对2PC的优化,暂时没有公认的实现
CAP 理论
CAP定理(CAP theorem),又被称作布鲁尔定理(Brewer's theorem),它指出对于一个分布式计算系统来说,不可能同时满足以下三点:
- 一致性(Consistency) (等同于所有节点访问同一份最新的数据副本)
- 可用性(Availability)(每次请求都能获取到非错的响应——但是不保证获取的数据为最新数据)
- 分区容错性(Partition tolerance)(以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择
因为网络不可靠性一直存在,所以一般是在AP和CP之前选择,个人感觉在实际应用中更多的是在不同的局部或全局场景中对一致性和可靠性的取舍;因为CAP理论在分布式发展中又无法保持数学论证上的纯粹性,慢慢变成了一个默认的方向上的指导,而不是绝对的或者是严谨的。
BASE
BASE 是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
- Basically Available(基本可用)
分布式系统在出现不可预知故障的时候,允许损失部分可用性。 - Soft state(软状态)
软状态也称为弱状态,和硬状态相对,是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。 - Eventually consistent(最终一致性)
最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
可靠事件队列(最大努力交付)
指的是一种依靠持续重试来保证可靠性的解决方案——最大努力交付(Best-Effort Delivery),一般还是算在BASE的下面,实现方式有多重,如使用消息表或者使用MQ到等等,可支持的消息框架可能有 RabbitMQ、RocketMQ。
RocketMQ 实现最大努力交付的示例
待实现
TCC 事务
TCC 是一种对业务侵入性比较强的分布式事务方案,每个分布式业务端分为以下3个阶段:
- Try:尝试执行阶段,完成所有业务可执行性的检查,预留好全部的业务资源;
- Confirm:确认执行阶段,直接使用Try阶段准备的资源来完成业务处理,需要支持幂等;
- Cancel:取消执行阶段,释放Try阶段预留的业务资源,也需要支持幂等;
TCC几乎不会涉及锁和资源的争用,具有很高的性能潜力。能保证最终一致性。
一些开源实现:Seata、ByteTCC、Himly、TCC-transaction。
SAGA 事务
Saga 是一种“长事务的解决方案”,更适合于“业务流程长、业务流程多”的场景。Saga分为两部分操作:
- 将大事务拆分为若干个小事务;
- 为每个子事务设计对应的补偿动作Ci;
Saga有两种恢复策略:
- 正向恢复:如果事务Ti提交失败,则一直对Ti进行重试,执行模式为:T1, T2, ..., Ti(失败), Ti(重试)..., Ti+1, ..., Tn.
- 反向恢复:如果事务Ti提交失败,则一直执行Ci对Ti进行补偿,直至成功,执行模式为:T1, T2, ..., Ti(失败), Ci(补偿), ..., C2, C1。
开源实现:Seata Saga模式;
AT事务模式
开源实现:Seata AT模式;