分布式事务
什么是分布式事务
在原来的分布式项目中要要求功能的单一性原则,数据库就会被分库,分布式事务就是解决不同数据库进行数据库事务操作的问题。
数据库事务性质:
ACID
A|原子性:不部分成功,要么全成功要么全失败
C|一致性:始终维持正确的状态
I|隔离性:并发事务不会相互干扰
D|持久性:事务提交结果会永久保存
案例
该案例看库存减少的时候是否会照成分布式事务的问题。
CAP定理:
- C (Consistency):一致性。指分布式系统在任一时刻,各节点的数据副本具有相同的值或包含相同的事务处理结果。
- A (Availability):可用性。指分布式系统能够无休止,无中断的服务处理请求。
- P (Partition tolerance):分区容错性。指分布式系统能够在网络并发故障的环境中依然正常工作。
根据CAP定理,分布式系统最多只能同时满足两个需求: - CA:可用且一致。典型系统为Vertica数据库,以数据一致性为优先考量,在故障时牺牲服务的可用性。
- CP:分区容错且一致。典型系统为mongoDB,Spanner,在出现网络分区后,优先选择维持数据一致性,部分节点的服务不可用。
AP:可用且分区容错。典型系统为Cassandra,DynamoDB,在出现网络分区后,节点之间的数据可能暂时不一致,但整体服务仍然可用。
BASE理论:
- Basically Available(基本可用):分布式系统在出现故障时, 允许损失部分可用性,及保证核心可用
- Soft State(软状态):在一段时间内,允许出现中间状态,比如临时的不一致问题
- Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。
而分布式事务最大的问题是各个子事务一致性的问题,因此可以借鉴CAP定理与BASE理论:
- AP模式:各个子事务分别执行和提交,允许出现不一致性,然后采取弥补措施恢复数据即可,实现最终一致。
- CP模式:各个子事务执行后互相等待,同时提交,同时回滚。达成强一致性。但是事务等待过程中处于弱等待状态。
解决分布式事务的思想和模型:
- 全局事务:整个分布式的事务,一般来说可能会加锁
- 分支事务:分布式事务中包含每个子事务系统的事务
- 最终一致思想:各分支事务分别执行并提交,通过又不一致就回滚或者在想其他方法恢复数据
- 强一致思想:各个分支事务执行完成业务不要提交,等待彼此结果。而后同意提交或者回滚。
SETA架构
Seta事务中三个重要的角色:
- TC(Transaction coordinator)-事务协调者:维护全局和分支的事务状态,协调全局事务的提交或者回滚。
- TM(Transaction Manager)- 事务管理器 :定义全局事务的范围,开始全局事务,提交或者回滚全局事务。
- RM(Resource Manager)-资源管理器:管理分支事务处理的资源,与TC交谈以及注册分支事务和报告分支事务的状态,并驱动分支事务的提交或回滚。
Seata的四种不同分布式解决方式:
- XA模式:强一致性分阶段事务模式,牺牲了可用性,无业务侵入。
- AT模式:最终样子hi的分解杜纳事务模式,无业务入侵,也是seata的默认模式
- TCC模式:最终一致的分阶段事务模式,有业务入侵
- SAGA模式:长事务模式,有业务入侵
XA模式(规范)
XA 规范是X/Open 组织定义分布式事务处理(DTP,Distributed Transaction Procession)标准,
XA规范描述了全局的TM与局部的RM之间的接口几乎所有数据库都支持XA规范。
SETA的XA规范
CP规范:并非完全强一致,可能在2.3时网络传输存在抖动,导致RM未收到消息,不执行
- 异常情况
预执行SQL,会将数据库相应的数据资源锁住,然后预先执行SQL。
一阶段:
- 事务协调者通知每个事物参与者执行本地事务
- 本地事务执行完成后报告事务执行状态给事务协调者,此时事务不提交,继续持有数据库锁
- 将执行状态提交给TC
二阶段:
- 事务协调者基于一阶段的报告来判断下一步操作
- 如果一阶段都成功,则通知所有事务参与者,提交事务
- 如果一阶段任意一个参与者失败,则通知所有事务参与者回滚事务
2.3虽然是预执行SQL但是SQL真的没有执行,它锁住的数据资源会释放
具体执行流程
Stage1:
- 外部接口调用分布式事务的接口,TR收到消息后去通知TC准备开启全局事务。
- TR通知RM准备执行分布式事务
- RM去TC上注册分布式事务。
- RM去预执行sql,并获取到数据库锁,此步对性能消耗较大,会在执行事务的时候会锁柱数据库而且必须要求有数据库能兼容XA模式才能执行。
- 等待所有的RM把事务都执行完成了,会向TC提交事务执行的状态。
Stage2:
- 当RM提交完成后TM会向TC发起消息,提醒TC当前事务已经执行完成了。
- TC回去检测RM执行事务的状态如何,如果都成功了,就让RM提交,如果哪怕有一个失败了,就让RM回滚。
- 按照TC的指示,RM看是去操作提交事务还是回滚,做完之后会释放数据库的锁。
XA模式的优点:
事务的强一致性,满足ACID
常用数据库都支持,实现简单,没有代码侵入
XA模式的缺点:
由于会锁住数据库资源,等待二阶段结束才会释放,性能较差
依赖关系型数据库实现事务。
AT模式
AT模式同样是分阶段提交的事务模型,不过弥补了XA模型中锁定资源周期过长的缺陷。
binlog:主从机数据同步,恢复数据。
undolog:记录用户操作前后的日志。
在innodb上,如果去要读数据,数据会从磁盘存到buffer pool里面,一次抓取的数据磁盘页未16kb,存入bufferpool,假设要对数据修改,mysql会先在内存里面修改,如果此时服务器挂了,ram数据丢失,磁盘未更新,可能会存在数据不一致问题。
redolog:会先记录我上次更改ram里面数据的日志,方便服务器挂了之后,未落盘好恢复。
undolog:bufferpool的内容也会存入undolog。记录数据最开始的样子,后面处理事务回滚。相当于数据的快照。
AT执行流程:
- Stage1:
1.1 都是由外界调用Transaction Manager接口通知Transaction Coordinator,
1.2 然后TM去做分支调用去调用Resource Manager的事务,
1.3 RM会去Transaction Coordinator注册分支事务
1.4 此时与XA模式不同的是,AT模式中RM会先做一份undolog的数据的快照然后直接把执行的事务提交,提交之后RM会再做另外一份的数据快照:redolog(异步,满足A)
1.5 然后再给TC说执行的状态 - Stage2:
2.1 事务提交完成后,TM会通知TC已经完成事务的提交。
2.2 TC会去查看时候有执行失败或者异常的事务,就让其回滚恢复log数据,执行成功的就删除log
AT模式脏写问题
由于线程1要去减10块,执行完提交事务放锁,然后线程2拿锁结果又减了10块钱,TC看线程1执行结果有问题就让事务回滚,结果就滚回了100,用户多赚10块。
解决方法:
在AT模式下会自动加上一把全局锁,配上一个全局锁的专用的表,该表在执行事务时会记录该事务的id,事务操作的表,以及操作该表对应的行数。
如图:在线程1获取到全局锁时候,执行并提交事务,在提交之前保存了一份数据快照,提交之后又保存了一份数据快照,之后释放DB锁,但是此时全局锁线程1还是拿着在,假设线程2回去执行相同的事务,按理来说会将钱减到80,线程2可以获取到DB锁,但是由于获取不到全局锁,开始自旋,AT设置的自旋重试机制为默认30次,间隔10ms,如果在这段时间内都没执行成功,该事务就执行失败,回滚然后释放DB锁。线程1就会获取到DB锁,就可以执行使用执行事务之后的数据进行快照恢复。大多数二阶段就是提交而不是回滚。
问题2:在极少数的情况下,可能会有不归全局锁管的事务进行对数据库表的操作导致账户金额产生幂等性问题。解决方法:当金额出现了问题就按照上述线程1执行完事务之后的快照恢复数据。
然后人工介入!!!
AT模式的优点:
一阶段完成直接提交事务,释放数据库资源,性能比较好
利用全局锁实现读写隔离
没有代码侵入,框架自动完成回滚和提交
AT模式的缺点:
两阶段之间属于软状态,属于最终一致
框架的快照功能会影响性能,但比XA模式要好很多
看面试官怎么问AT的性能为什么会比较低?
由于多个人购买同件商品,比如秒杀。
但是多人购买多样不同的商品性能比XA好。
TCC模式:极致性能!
相对于AT,TCC在1.4的时候开Try,会检查资源与预留
最后TC提交的时候,要么comfirm要么cancle
TCC空回滚与业务悬挂
-
空回滚
在 try 阶段服务 1.4下方 这个节点发生了故障,try 阶段在不考虑重试的情况下,全局事务必须要走向结束状态,这样就需要在执行一次 cancel 操作。但是下方节点宕机,如果当节点恢复服务功能时,都执行cancle操作,下方节点就会出现幂等问题,所以要让该宕机节点执行一次空回滚。 -
业务悬挂
悬挂是指因为网络问题,RM 开始没有收到 try 指令,但是执行了 Rollback 后 RM 又收到了 try 指令并且预留资源成功,这时全局事务已经结束,最终导致预留的资源不能释放。queryTCCFenceDO 方法 sql 中使用了 for update,这样就不用担心 Rollback 方法中获取不到 tcc_fence_log 表记录而无法判断 try 阶段本地事务的执行结果了。