一文彻底弄懂分布式事务里的最终一致性

这篇文章将介绍什么是分布式事务,分布式事务解决什么问题,对分布式事务实现的难点,解决思路,不同场景下方案的选择,通过图解的方式进行梳理、总结和比较 。
相信耐心看完这篇文章,谈到分布式事务,不再只是有“2PC”、“3PC”、“MQ的消息事务”、“最终一致性”、“TCC”等这些知识碎片,而是能够将知识连成一片,形成知识体系 。
1 什么是事务介绍分布式事务之前,先介绍什么是事务 。
事务的具体定义
事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元,组成事务的所有操作只有在所有操作均能正常执行的情况下方能提交,只要其中任一操作执行失败,都将导致整个事务的回滚 。
简单地说,事务提供一种“ 要么什么都不做,要么做全套(All or Nothing)”机制 。

一文彻底弄懂分布式事务里的最终一致性

文章插图
 
数据库事务的ACID属性事务是基于数据进行操作,需要保证事务的数据通常存储在数据库中,所以介绍到事务,就不得不介绍数据库事务的ACID特性,指数据库事务正确执行的四个基本特性的缩写 。包含:
  • 原子性(Atomicity) 整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节 。事务在执行过程中发生错误,会被 回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样 。例如:银行转账,从A账户转100元至B账户,分为两个步骤:
  • (1)从A账户取100元
  • (2)存入100元至B账户 。这两步要么一起完成,要么一起不完成,如果只完成第一步,第二步失败,钱会莫名其妙少了100元 。
  • 一致性(Consistency) 在事务开始之前和事务结束以后,数据库数据的一致性约束没有被破坏 。例如:现有完整性约束A+B=100,如果一个事务改变了A,那么必须得改变B,使得事务结束后依然满足A+B=100,否则事务失败 。
  • 隔离性(Isolation) 数据库允许多个并发事务同时对数据进行读写和修改的能力,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交,它所访问的数据就不受未提交事务的影响 。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致 。例如:现有有个交易是从A账户转100元至B账户,在这个交易事务还未完成的情况下,如果此时B查询自己的账户,是看不到新增加的100元的 。
  • 持久性(Durability) 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失 。

一文彻底弄懂分布式事务里的最终一致性

文章插图
 
本地事务ACID大家应该都知道了,统一提交,失败回滚,严格保证了同一事务内数据的一致性!而分布式事务不能实现这种ACID,它只能实现CAP原则里的某两个,CAP也是分布式事务的一个广泛被应用的原型,CAP(Consistency, Availability, Partition Tolerance), 阐述了一个分布式系统的三个主要方面, 只能同时择其二进行实现. 常见的有CP系统, AP系统 。
应用于CP和AP的原则在业界出现了一些框架:
CP系统就有二阶段提交(强一致性)
一文彻底弄懂分布式事务里的最终一致性

文章插图
 
AP系统就有TCC(补偿型事务)
一文彻底弄懂分布式事务里的最终一致性

文章插图
 
其中最近接触的aspnetcore.cap就是一个满足最终一致性的异步消息方案实现的,其中它为MySQL,sqlserver都提供了解决方案,消息队列可以有kafka和rabbitmq两种选择,根据自己的需要去安装,源代码在github上有开源,nuget上也有对应的包包!
方案简介本地消息表的方案最初是由ebay提出,核心思路是将分布式事务拆分成本地事务进行处理 。
方案通过在事务主动发起方额外新建事务消息表,事务发起方处理业务和记录事务消息在本地事务中完成,轮询事务消息表的数据发送事务消息,事务被动方基于消息中间件消费事务消息表中的事务 。
这样设计可以避免”业务处理成功 + 事务消息发送失败",或"业务处理失败 + 事务消息发送成功"的棘手情况出现,保证2个系统事务的数据一致性 。
处理流程下面把分布式事务最先开始处理的事务方成为事务主动方,在事务主动方之后处理的业务内的其他事务成为事务被动方 。
为了方便理解,下面继续以电商下单为例进行方案解析,这里把整个过程简单分为扣减库存,订单创建2个步骤,库存服务和订单服务分别在不同的服务器节点上,其中库存服务是事务主动方,订单服务是事务被动方 。


推荐阅读