面试专题之——分布式事务

什么是分布式事务?为什么要使用分布式事务?

在某个业务中需要对数据库进行多次写操作时,为了防止出错我们都会使用本地事务,但在分布式架构下本地事务就无法满足需求了。分布式事务就是实现了跨服务,跨数据源的ACID

分布式事务有哪些解决方案?

二阶段提交

1、协调者下发指令让每个参与者执行事务

2、参与者执行完后通知协调者“我执行完了”,但不释放本地资源,同时记录redo log与undo log

3、所有参与者执行完之后,协调者下发提交指令(如果出错则发送回滚指令)

4、所有参与者提交事务或者回滚事务

存在问题:

1、单点故障:无论提交事务前还是执行事务前,任何一个发生故障都会导致无法实现分布式事务

2、事务参与者在全部参与者执行完成之前都会锁定本地资源,导致阻塞,最终导致效率低

TCC模式

它与二阶段提交的区别是,每个参与者执行完事务后直接进行本地事务提交,如果需要回滚,则执行业务中程序员写的补偿代码

它可以解决二阶段提交锁定资源的问题,它采用的是以补偿型事务的思想,并且也是性能最好的分布式事务解决方案。但是因为要程序员手动写事务补偿部分的代码,会导致开发难度高、代码侵入等问题

可靠消息服务

1、事务发起者开启本地事务、发送事务的MQ消息,并在数据库中进行记录,最后提交事务

2、事务执行者接收到这个事务的消息,然后开启本地事务、处理事务相关业务、修改数据库中的记录,最后提交本地事务

这里用到了RabbitMQ的消息确认机制,实现了消息生产与消费的可靠性:

生产者确认机制:发送消息到MQ时,设置一个异步监听器,监听来自MQ的ack,如果失败则进行重试等操作

消费者确认机制:消费者监听队列时指定手动ack模式,只有消费者手动确认了ack才删除消息

这样就实现了最终的一致性

优点:

  • 相对TCC代码侵入少
  • 业务相对简单
  • 多个本地事务执行完成都直接提交,保证最终的一致性,性能好

缺点:

  • 依赖MQ的可靠性
  • 事务无法回滚
  • 事务时效性差

seata - AT模式

它是一种无侵入的分布式事务解决方案,可以看作是TCC的一种优化,它解决了代码侵入与复杂的问题

它分为三个部分:

  • TC:协调者,这里是seata server
  • TM:业务模块中全局事务的开启者
  • RM:业务模块中的执行者

执行原理:

  • 一阶段:

    1、TM开启事务,并向TC申请全局事务,生成XID信息

    2、TM所在服务调用其他RM服务

    3、seata拦截sql,并且解析sql,找到更新前的业务数据生成before_image

    4、RM执行本地事务

    5、seata拦截sql,并且解析sql,找到更新后的业务数据生成after_image

    6、生成unlog_log存放至数据库

    7、各个RM执行事务,并告知TC自己的事务执行结果

    8、获取全局行锁,保证分布式事务的原子性

    9、释放本地锁

  • 二阶段:

    1、TC统计所有RM的事务执行情况

    2、全部成功则让所有RM清空before_image与after_image,并释放全局锁

    3、有RM执行事务失败,则进行进行回滚,回滚需要用after_image与当前数据对比,如果数据不一致,出现脏数据,那么请求人工介入。如果没有脏数据,则让所有RM根据before_image进行回滚,回滚之后让所有RM清空before_image与after_image,最后释放全局锁

如何尽量避免脏数据?

首先因为全局锁的机制保证了事务的原子性自然的减少了脏数据,但还是不可避免的有不使用分布式事务的边缘业务可能会修改数据库导致脏数据,可以用分布式锁来解决这个问题。