面试题——分布式事务的实现方式

面试题

分布式事务了解吗?你们是如何解决分布式事务问题的?

面试官心理分析

只要你聊到了分布式系统,就一定会问道分布式事务,如果你对分布式事务一无所知的话,那的确很坑爹,至少要知道分布式事务的大致解决方案,每个方案的优缺点。
现在的面试,分布式系统成了标配,分布式事务也就成了必问的问题了。每种分布式方案都会带来一些问题,比如TCC方案的网络问题,XA方案的一致性问题。

面试题剖析

分布式事务的实现主要有以下5种方案:

  • XA方案
  • TCC方案
  • 本地消息表
  • 可靠消息最终一致性方案
  • 最大努力通知方案

二阶段提交方案/XA方案

所谓的XA方案,即:二阶段提交,有一个事务管理器的概念,负责协调多个数据库(资源管理器)的事务,事务管理器先问问各个数据库你准备好了吗?如果每个数据库都回复ok,那么久正式提交事务,在各个数据库上执行操作;如果任何一个数据库回复不ok那么就回滚事务。

这种分布式事务方案比较适合在单块应用里,跨多个库的分布式事务,而且因为严重依赖于数据库层来保证复杂的事务,效率很低,不适合于高并发的业务场景。如果要使用可以基于Spring + JTA来使用。

这个方案其实很少使用,一般来说某个系统内部如果出现了跨多个库的这么一个操作,是不合规的。现在微服务,一个大的系统分成几十个甚至上百个服务。一般来说,我们的规定和规范,是要求每个服务只能操作自己对应的数据库。

如果要操作别人的数据,必要通过调用别人的接口来实现,绝对不允许交叉访问别人的数据库。

TCC方案

TCC的全程就是TryConfirmCancel

  • Try阶段:这个阶段说的是对各个服务的资源坐检测以及对资源进行锁定或者预留
  • Confirm阶段:这个阶段说的是在各个服务种执行实际的操作
  • Cancel阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经直径成功的业务逻辑的回滚操作。

说实话这种方法也是用的比较少,但是也是有使用的场景。因为这个事务回滚实际上是严重依赖于你自己写代码来回滚和补偿了,会造成补偿代码量巨大。

比如说一些跟钱相关的业务,还是可以用的,严格保证了事务要么全部成功,要么全部回滚,这样保证了资金的正确性,保证不会在资金上出问题。

而且最好是各个业务执行的时间都比较短。

但是说实话,一般尽量别这么搞,自己手写回滚逻辑,或者是补偿机制,业务代码是很难维护的。

本地消息表

本地消息表其实是外国ebay搞出来的一套思想。

这个大概意思是这样的:

  1. A系统在自己本地一个事务里操作的同时,插入一条数据到消息表
  2. 接着A系统将这个消息发送到MQ中去
  3. B系统接收到了消息之后,在一个事务里,往自己本地消息表中插入一条数据,同时执行其他的业务操作,如果这个消息已经被处理过了,那么此时这个事务会回滚,这样保证不i会重复处理消息
  4. B系统执行成功之后,就会更新自己更新自己本地的消息表的状态,那么此时A系统会定时扫描自己的消息表,如果有未处理的消息,会再次发送到MQ中去,让B再次处理
  5. 这个方案保证了最终一致性,哪怕B事务失败了,但是A会不断重发消息,直到B那边成功为止

这个方案说实话最大的问题就在于严重依赖于数据库的消息表来管理事务,如果是高并发场景怎么办?

可靠消息最终一致性方案

这个的意思,就是干脆不要本地消息表了,直接基于MQ来实现事务。比如阿里的RocketMQ就支持事务。

大概意思是:

  1. A系统先发送一个prepare消息到mq,如果这个prepare发送失败就直接取消执行操作了。
  2. 如果这个消息发送成功了,那么接着执行本地事务,如果成功就告诉MQ发送确认消息,如果失败就告诉MQ发送回滚消息
  3. 如果发送了确认消息,那么此是B系统会接收到确认消息,然后执行本地事务
  4. MQ会自动定时轮询所有prepared消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确定的消息,是继续重试还是回滚?一般来说这里你就可以查以下本地数据库看之前的消息是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,而确认消息缺发送失败了。
  5. 这个方案里,要是系统B的事务失败了怎么办?会自动不断重试直到成功,如果实在不行就要对资金类的业务进行回滚,比如系统B回滚了之后,想办法通知系统A也回滚,或者报警由人工干预。
  6. 要么使用RocketMQ的机制,要么自己实现一套类似的方案。

最大努力通知方案

这个方案的大致意思就是:

  1. 系统A本地事务执行完之后,发送消息到MQ
  2. 这里会有一个消费MQ的最大努力通知服务,这个服务会消费MQ然后写入数据库中记录下来,或者是放入某个内存队列也可以,接着调用系统B的接口
  3. 要是系统B执行成功就ok了。要是系统B执行失败了,那么最大努力通知服务就定时尝试重新调用系统B,达到一定重复次数之后,还是不行就放弃。

你们公司是如何处理分布式事务的?

如果真的被问到,就可以说,我们某个场景特别严格,用的是TCC来保证强一致性,然后其他的一些场景基于阿里的RocketMQ来实现分布式事务。

找一个严格资金要求绝对不能出错的场景,你可以说你用的是TCC方案,如果是一般的分布式场景,订单服务插入之后要调用库存服务更新库存,库存数据没有资金的那么敏感,可以依靠最终一致性方案。

发布了17 篇原创文章 · 获赞 0 · 访问量 319

猜你喜欢

转载自blog.csdn.net/qq_26375325/article/details/105371663