简单的说,就是一次大操作由不同小操作组成,这些小操作分布在不同服务器上,分布式事务需要保证这些小操作要么全部成功,要么全部失败.
两阶段提交简称2PC(two phase commitment)
注: 两阶段提交方案锁定资源时间长,对性能影响很大,基本不适合解决微服务事务问题.
两阶段提交协议是基于XA规范, 阻塞, 属于刚性事务
数据库实现(XA, MySQL和Oracle都支持)
xa_start, xa_end, xa_prepare, xa_commit, xa_rollback
TCC(Try Confirm Cancel), 是2PC的一种改进
事务开始时,业务应用会向事务协调器注册启动事务。之后业务应用会调用所有服务的try接口,完成一阶段准备。之后事务协调器会根据try接口返回情况,决定调用confirm接口或者cancel接口。如果接口调用失败,会进行重试。
TCC方案让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。 当然TCC方案也有不足之处,集中表现在以下两个方面:
消息一致性方案是通过消息中间件保证上、下游应用数据操作的一致性。基本思路是将本地操作和发送消息放在一个事务中,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。
消息方案从本质上讲是将分布式事务转换为两个本地事务,然后依靠下游业务的重试机制达到最终一致性。基于消息的最终一致性方案对应用侵入性也很高,应用需要进行大量业务改造,成本较高。
Fescar(Fast & EaSy Commit And Rollback), 升级后为: Seata(Simple Extensible Autonomous Transaction Architecture)
问题1: 如果把下单操作和把下单消息放到MQ的操作放到一个try-catch块中
try {
// 下单
orderService.createOrder();
// 发送消息到MQ
msgClient.sendMsg(orderId);
} catch (Exception e) {
}
发送消息是网络操作, 网络操作一般会有3中结果: success, fail, timeout. Success 和 fail都相对好处理, 但是timeout是不知道消息发送成功还是失败的.所以这种操作是不合理的.
解决方法: 一般会先把下单成功的消息放入DB中, 然后从DB中取数据放入MQ
分布式缓存和数据库的一致性4步骤:
Saga模式是现实中可行的方案,采用事务补偿机制。每个本地事务都存储一个副本,如果出现失败,则利用补偿机制回滚。
TCC模型和saga模型
TCC(Try, Confirm, Cancel), 以A向B账户转账为例, 分为汇款服务和收款服务
Try:
Confirm:
不做任何操作
Cancel:
Try:
检查B账户的有效性
Confirm:
Cancel:
不做任何操作
saga模型:
把一个长事务拆分成多个短事务(本地事务), 每个事务都有对应的执行模块和补偿模块(对应TCC中的Confirm 和 Cancel)
saga vs TCC
区别在于TCC多了一个Try(预操作), 每次都会预扣减资源. saga虽然也有Try操作, 但是只是做一些检测操作
刚性事务vs 柔性事务
刚性事务(XA模型) | 柔性事务 | |
---|---|---|
实际项目中有无应用场景 | 无 | 有 |
回滚 | 支持 | 通过补偿支持 |
一致性 | 强一致性 | 最终一致性 |
隔离性 | 原生支持 | 实现资源锁定接口(如信用卡预授权) |
并发性能 | 低, 严重衰退(锁定资源时间太久) | 略微衰退 |
适合场景 | 短事务,并发较低 | 长事务, 高并发 |
SET lock_key random_value NX PX 5000
注: redis从本质上说是AP模型, 只保证可用. 如果需要用分布式锁, 必须是CP模型, 需要保证一致性.etcd可以保证.
缓存不可用, 查询数据库,
做好评估: 缓存宕机, 评估数据库压力
该篇blog并不代表该知识点的所有内容, 在今后的工作学习中, 持续更新! 如对blog中的观点有异议/建议,请发送email至: [email protected], 感谢您的阅读.