根据业务需求需要对业务进行拆分,例如将一个大应用拆分成用户模块,订单模块,商品模块,每个模块都有自己的数据库,在用户购买商品的时候需要扣减商品模块库存,在订单模块添加订单数据,这时候需要保证这两个数据库操作在同一个事务中完成,因此就出现了分布式事务
1. LCN事务模式
一、原理介绍:
LCN模式是通过代理Connection的方式实现对本地事务的操作,然后在由TxManager统一协调控制事务。当本地事务提交回滚或者关闭连接时将会执行假操作,该代理的连接将由LCN连接池管理。
二、模式特点:
该模式对代码的嵌入性为低。
该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。
2. TCC事务模式
一、原理介绍:
TCC事务机制相对于传统事务机制(X/Open XA Two-Phase-Commit),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。主要由三步操作,Try: 尝试执行业务、 Confirm:确认执行业务、 Cancel: 取消执行业务。
二、模式特点:
该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
该模式对有无本地事务控制都可以支持使用面广。
数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。
3. TXC事务模式
一、原理介绍:
TXC模式命名来源于淘宝,实现原理是在执行SQL之前,先查询SQL的影响数据,然后保存执行的SQL快走信息和创建锁。当需要回滚的时候就采用这些记录数据回滚数据库,目前锁实现依赖redis分布式锁控制。
二、模式特点:
该模式同样对代码的嵌入性低。
该模式仅限于对支持SQL方式的模块支持。
该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。
该模式不会占用数据库的连接资源。
————————————————
为了解决分布式一致性问题,前人在性能和数据一致性的权衡过程中总结了许多经典的协议和算法。比较著名的有:2PC、3PC、TCC、Paxos、Raft、Zab、ISR。除了这些之外,业界用的最多的其实是基于MQ实现的。
2PC(Two Phase Commit)两阶段提交:一般说的两阶段提交是基于XA协议的。另外JTA协议的也比较常见。
XA是一个分布式事务协议。它大致分为两部分:事务管理器和本地资源管理器。其中本地资源管理器往往由数据库实现,比如Oracle、DB2都实现了XA接口。MySQL对XA的支持不是很好。而事务管理器作为全局的调度者,负责各个本地资源的提交和回滚。
两阶段提交的优点是:原理简单、实现方便。缺点是同步阻塞、单点问题、数据不一致。
3PC(Three Phrase Commit)三阶段提交:分为CanCommit、PreCommit、Do Commit 三个阶段。就是把两阶段提交的Phase 1分成两个,预提交的时候如果有参与者返回No或者超时则中断事务。
三阶段提交的优点是降低参与者阻塞范围,并能够在出现单点故障后继续达成一致。缺点是因为preCommit阶段,在这个阶段如果出现网络分区,协调者无法与参与者正常通信,参与者仍然会进行实物提交,造成数据不一致。
TCC(Try-Confirm-Cancel)
Try:完成所有的检查,预留必须资源
Confirm:使用Try阶段预留的资源执行业务,如果执行出现异常,要重试
Cancel:释放Try阶段预留资源
TCC能够对分布式事务中的各个资源进行分别锁定,分别提交与释放。适用于严格一致、执行时间短、实时性要求高的场景。
Paxos算法:之前看过《从Paxos到Zookeeper》那本书,没怎么看明白。实现比较复杂,Zookeeper就是用这个来实现的分布式一致性。Paxos算法、Raft协议和Zab(Zookeeper Atomic Broadcast)协议都是一种通过多数投票来保证主备数据一致性的。
ISR(In-Sync Replicas)机制:Kafka使用了这个机制来保证数据一致性。ISR认为对于2f+1个副本来说,多数投票机制要求最多只能允许f个副本发生故障,如果要支持2个副本的容错,则需要至少维持5个副本。
基本MQ实现是一种异步确保型的实现方案。将同步阻塞的事务变成异步的,避免了对数据库事务的争用。
2.X/OpenDTP事务模型
概念: X/Open是一个组织机构,定义了一套分布式事务标准,定义了规范的API接口角色:
AP application
RM resouces manager 资源管理器
TM transaction manager事务管理器
3.Mysql事务处理过程
3.1 记录redo日志和undo日志,确保日志在磁盘上的持久化
3.2 更新数据记录
3.3 提交事务,redo写入commit记录
4. 2PC协议
两阶段提交
阶段一:提交事务请求
TM向所有的AP发送事务内容,询问是否可以执行事务的提交操作,并等待各个AP的响应
执行事务
各个AP节点执行事务操作,将undo和redo信息记录到事务日志中,尽量把提交过程中所消耗时间的操作和准备都提前完成后确保后续
事务提交的成功率
各个AP向TM反馈事务询问的响应
各个AP成功执行了事务操作,那么反馈给TM yes的response;如果AP没有成功执行事务,就反馈TM no的response
阶段二: 执行事务提交
存在的问题
数据一致性问题:当AP和TM都宕机了,新选出来的TM不知道宕机的AP到底做了什么操作,如果根据剩下的两个存活的AP的操作(commit或者abort)作为判断,则很可能会因为宕机的操作和存活的操作不一致导致数据不一致
同步阻塞:第二阶段中,如果所有的AP都宕机了.则TM只能阻塞等待AP的返回
5.3PC协议
阶段一:canCommit:TM请求AP是否可以commit
阶段二:preCommit
阶段三:doCommit
6.分布式事务的实现
atomikos
7.MQ实现最终一致性
7.1消息重复消费解决方案
幂等校验:在消费端新建一张表,使用唯一索引,消费数据前往数据表插入一条数据,如果插入成功则证明是第一次消费,如果插入失败则证明已经不是第一次消费
日志表来判断,或者通过状态锁来判断,消费端创建一个日志表,消费时插入一个id和状态,消费成功后去更新状态
最终一致性模式:
查询模式
补偿模式:A调用B,失败后自动重试,回滚原有操作,若不能自动重试则通知运营,人工补偿(如人工对账),或通知技术,监控,预警
TCC事务模型
8.LCN实现
服务消费方加注解 @TxcTransaction(timeout = 1000 * 10)
服务提供方获取全局事务ID,并绑定到上下文
public int updateStock(OrderDO orderDO) {
//获取全局事务ID,并绑定到上下文
String xid = RpcContext.getContext().getAttachment("xid");
TxcContext.bind(xid,null);
//执行自己的业务逻辑
int ret = jdbcTemplate.update("update stock set amount = amount - ? where product_id = ?",new Object[]{orderDO.getNumber(), orderDO.getProductId()});
TxcContext.unbind();
return ret;
}