铿然架构 | 作者 / 铿然一叶 这是 铿然架构 的第 110 篇原创文章
seata支持的事务模式有哪些?
A:4种模式,分别是AT、XA、TCC、SAGA。
undo日志的生命周期,何时生成,入库,清理?
A:undo日志在SQL执行的前后分别生成before、after镜像,接着在SQL执行后写入缓存;在本地事务提交前将undo日志入库;在本地事务提交之后或本地事务回滚后清理undo日志缓存。
全局锁是否会写undo日志?
A:不会,全局锁的作用是避免和全局事务操作的表一样,同时操作时导致脏写或脏读,它只涉及加锁操作,不会写undo日志。
undo日志入库前会经过哪些处理?
A:会做编码和数据压缩,编码支持Kryo、Protostuff、Fastjson、Fst、Jackson,数据压缩支持ZIP、SevenZ、Deflater、Lz4、Gzip、BZip2。
delete语句的undo日志镜像和insert、update语句有什么不同?
A:delete语句只有before镜像,没有after镜像,insert语句没有before镜像,update语句before镜像和after镜像都有。
undo日志的镜像格式?
A:具体的格式不需要记录,只需要知道核心原理则可。
语句类型 | 镜像原理 |
---|---|
insert | 记录插入数据的主键信息,undo时根据主键删除。 |
update | 记录更新前的数据和主键信息,updo时根据这些信息更新回去;同时要记录更新后的数据和主键信息,用于在undo时比较数据是否发生变化,如果没有变化才做undo操作。 |
delete | 记录删除的数据信息,在undo时做插入操作。 |
undo日志写入失败会如何处理?
A:undo日志写入失败会抛出SQLExection,所有异常都会向上抛出,最后触发全局事务回滚。
undo日志如何保证和业务操作在一个事务内?
A:undo日志和业务操作使用的是同一个connection实例,undo日志在connection做commit前写入,保证和业务操作执行的SQL一起提交。
undo模式的性能影响?
A:增加了新的SQL日志写入,高并发场景肯定会有影响,这个建议做一个性能测试,通过性能评估确定什么场景适合使用。
使用seata时,对异常处理有什么注意事项?
A:发生异常时,seata捕捉了Throwable进行处理,因此所有异常都会捕捉到,哪怕是诸如不需要添加catch块的RuntimeException,因此只需要注意不要自行捕捉异常,进行处理后不继续向上抛则可。
不想使用全局事务,又想在多个微服务调用时避免脏写有什么办法?
A:可以使用全局锁,注意是在分布式事务场景(多个微服务)下使用,如果只有一个微服务,本地事务就可以控制,不需要使用全局锁。
AT模式下,开启全局事务后,嵌套服务如何开启新的全局事务?
例如服务调用顺序A、B、C、D,A和D是一个事务,B和C要开启一个新的全局事务。
A:在B的入口方法上使用GlobalTransactional注解,并将propagation属性设置为Propagation.REQUIRES_NEW。
GlobalTransactional注解和Transactional注解的关系?
A:前者是全局事务注解,后者是本地事务注解,两者不冲突,可以同时使用。
TCC模式下,应该如何实现prepare,commit,rollback方法,才能保证事务一致性?
A:如果要分成多步来实现,可能undo日志是最保险的。网上提到TCC模式的一种实现方法是资源预留,在prepare时预留资源,commit时才实际扣除资源,rollback时删除预留资源。这种方式虽然比什么都不做好,但是commit也存在失败可能,也需要额外的处理机制,也并非什么场景下都适合。
不管哪种实现方式主要考虑点有:
● 实时性,是否要立即给用户返回结果,例如账户充值后要立即告诉用户充值后的余额
● 最终一致性,是立马要保证事务一致,还是最终一致就可以。
以上两点会影响实现方式,根据实际业务述求设计对应的方案,只要能达到目的则可。
如何选择TCC和AT模式?
A:AT模式下每一个分支事务都要写undo日志,全部交给框架去控制,而TCC模式只是提供了控制逻辑(协调执行各个分支事务的prepare、commit、rollback方法),具体的事务控制逻辑由业务实现,更加自由,轻量,例如:
● TCC可以只写更少的undo日志,比如只是在业务调用的入口处,记录一条交易请求日志,后面通过这条交易请求来构造undo日志,不需要每个表的每次操作都记录undo日志,也不需要同时记录before、after镜像,这样大大提高了性能。这样的操作方式在特定场景下完全可以使用(只要满足交易请求能构造出undo日志),而AT模式下的undo日志只是更通用,并且conver了所有表操作的场景,性能没有影响的场景下可以使用。
● 可以决定什么也不做,在整个分布式事务过程中,有的场景下,事务(或者部分分支事务)是必须要完成的,此时TCC模式就可以灵活控制某些分支是否需要实现commit或rollback方法。
简而言之,TCC更加灵活,经过定制后性能可能比AT模式高,但是复杂性高于AT模式,AT模式性能差,通用性高。
AT模式下事务传播级别有哪些,分别怎么处理的?
序号 | 类型 |
---|---|
NOT_SUPPORTED | 挂起已有的全局事务,执行当前操作,并返回结果 |
REQUIRES_NEW | 挂起已有的全局事务,并开启一个新全局事务,现有的全局事务继续执行 |
SUPPORTS | 如果全局事务不存在,则执行当前操作,并返回结果 |
REQUIRED | 现有全局事务继续执行 |
NEVER | 如果全局事务存在,则抛出异常,否则执行当前操作,并返回结果 |
MANDATORY | 如果全局事务不存在,则抛出异常 |
对于事务传播级别,需要思考什么场景会用哪种级别。
如何做到TC高可用?
A:通过K8S部署多副本实现高可用,并且通过服务发现机制和TM、RM通信。
是否要使用分布式事务?
A:分布式事务处理起来要么性能有影响,要么逻辑复杂,如果可以避免应尽量避免,主要手段有:
● 增加可靠性:包括数据库冗余,网络冗余(多网卡,多运营商专线),微服务冗余,减少交易处理失败需要回滚概率。
● 重试机制:如果节点A微服务不可用,可以重试发送到其他节点。并且服务交易要支持幂等性,保证重试时不会重复处理。
● 提供对账机制:特别是外部系统和内部系统,系统和系统之间,如果因为系统/网络不可用导致超时,有限次数重试之后还是失败导致的事务不一致,需要进行交易日志对账,做到最终一致性。
● 异步处理,先发送交易请求并记录下来,异步根据交易请求分步骤去处理,失败了则不断重试,始终失败则人工干预,最终保持一致性。
至于是否使用分布式事务,取决于每种方案下的成本,这个成本包含硬件投入,软件开发/维护,以及运营成本等,要综合考虑。
其他阅读:
萌新快速成长之路
如何编写软件设计文档
JAVA编程思想(一)通过依赖注入增加扩展性
JAVA编程思想(二)如何面向接口编程
JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则
JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?
Java编程思想(七)使用组合和继承的场景
JAVA基础(一)简单、透彻理解内部类和静态内部类
JAVA基础(二)内存优化-使用Java引用做缓存
JAVA基础(三)ClassLoader实现热加载
JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比
JAVA基础(五)函数式接口-复用,解耦之利刃