seata是什么?
seata是阿里巴巴开发的开源的分布式事务解决方案,支持XA、AT、TCC、Saga模式下的分布式事务。
拓展知识:常见的分布式事务解决方案
- seata(四种都支持,主推AT)
- 消息队列(TCC)
- saga
- XA
为什么需要seata?
在分布式框架下,不同的服务对应不同的数据库,如果涉及到跨服务调用的业务,传统的单机应用下的本地事务显然是不能满足跨数据库的数据一致性,因此分布式事务思想产生了,目前主要有四种实现思路 XA、AT、TCC、Saga;
而具体的落地实现的解决方案中 seata最流行。
优缺点
优点:AT模式下代码侵入性小,使用简单,只需要一个注解@GlobalTransaction即可;
缺点:默认工作在读未提交的隔离级别下,可能造成脏读,且就算按照官方的解决办法(在非全局事务方法上增加注解),也会增加性能开销,如果业务系统对性能有要求则要斟酌考虑。
原理
AT模式
- 同XA模式类似,不过在第一阶段提交了本地事务(且额外记录快照 before和after),第二阶段可根据快照来做回滚;
- 由于在第一阶段本地事务就commit了,释放了本地锁,所以性能比XA更好,但隔离性又如何保证呢?seata为解决隔离性的问题,增加了全局锁机制(在一阶段commit前获取全局锁 二阶段结束时释放全局锁),从而保证并发情况下两个事务间的写隔离,如果是非全局事务方法,也至少要使用@GlobalLock注解+select for update语句触发全局锁来避免脏读,而脏写则无法避免(除非改为全局事务方法),但seata的AT模式下的回滚阶段会比较目标记录是否被脏写(before-image == 最新记录值),如果被脏写则回滚失败,抛出异常!
- 总结:
- 一阶段:解析sql并查询before-image、执行sql、查询after-image、记录undo log、获取全局锁、commit、上报执行结果给协调者;
- 二阶段-提交:异步删除undo log记录;
- 二阶段-回滚:获取undo log记录、检查是否存在脏写,如果是则根据配置策略来做处理、回滚、删除undo log。
- 优点:
- AT模式不依赖数据库底层的XA,更灵活;
- 一阶段就释放了本地锁,不与非全局事务方法锁冲突,性能更好;
- 没有代码侵入,使用方便;
- 缺点:
- 最终一致性,中途可能发生脏写、脏读。
- 额外的自动操作会影响性能,但也比XA模式好很多;
TCC模式
- 原理:Try Commit Cancle的缩写,需要手动编写三个阶段的业务代码 预留资源(try)、提交资源(commit)、取消预留(cancle),且需要注意 业务悬挂、空回滚、失败重试时的幂等性 问题。
- 优点:
- 完全不依赖于数据库,redis库也能支持,够灵活!
- 相较于AT模式没有全局锁,也不需要undo log,所以性能更好!
- 因为try阶段的冻结操作,天生有隔离性;
- 缺点:
- 代码编写很麻烦,比较费程序员!更适用于对性能要求高的业务;
- 使用场景局限于扣减余额/库存之类的业务;
- 一些联想:try阶段一般是锁定库存的操作,这个操作让我想起了
- 物资系统中的 发起领用申请后锁定库存 审批通过后扣减锁定的库存 否则取消锁定;
- 网购场景中的 提交订单后锁定库存 支付成功后扣减库存 否则取消库存锁定;
- 以上两条是属于业务上的三阶段提交;
- 系统接口代码层面的三阶段:调用下单总接口 -> 账户服务TCC -> 库存服务TCC -> 加积分服务TCC/AT -> 返回购买成功!(seata中TCC和AT可以混合使用)
Saga模式
- 原理:一阶段直接提交业务,二阶段如果是提交则什么都不做,如果是回滚则执行手动编写的回滚逻辑。
- 优点:
- 事务参与者可以基于事件驱动实现异步调用,吞吐高;
- 一阶段直接提交事务,没有锁,性能好;
- 相对TCC模式 只需额外编写回滚逻辑,相对不那么麻烦;
- 缺点:
- 软状态持续时间不确定,时效性差?
- 没有任何隔离性保证,两个阶段之间的时刻,可能会发生脏写/脏读;
四种模式对比图
参考链接
面试问题
- seata支持哪几种分布式事务模式?答:四种 XA、AT、TCC、Saga
- AT模式下seata是如何解决脏写/脏读问题的?答:全局锁机制
- AT模式下如果发生了脏写导致回滚失败,如何处理?答:首先应该尽量避免脏写(加入全局事务或全局锁)。回滚失败会抛异常并记录日志,可人工根据日志提示修正错误数据或删除对应undo(可自定义实现FailureHandler做邮件通知或其他)
- TCC模式下,怎么保证事务隔离性的?答:冻结操作就是隔离