分布式事务的前世今生和姻缘化解(完整版)
一、分布式事物由来
微服务的发展导致的问题
微服务倡导将复杂的单体应用拆分为若干个功能简单、松耦合的服务,这样可以降低开发难度、增强扩展性、便于敏捷开发。当前被越来越多的开发者推崇,很多互联网行业巨头、开源社区等都开始了微服务的讨论和实践。Hailo有160个不同服务构成,NetFlix有大约600个服务。国内方面,阿里巴巴、腾讯、360、京东、58同城等很多互联网公司都进行了微服务化实践。当前微服务的开发框架也非常多,比较著名的有Dubbo、SpringCloud、thrift 、grpc等。
虽然微服务现在如火如荼,但对其实践其实仍处于探索阶段。很多中小型互联网公司,鉴于经验、技术实力等问题,微服务落地比较困难。如著名架构师Chris Richardson所言,目前存在的主要困难有如下几方面:
1)单体应用拆分为分布式系统后,进程间的通讯机制和故障处理措施变的更加复杂。
2)系统微服务化后,一个看似简单的功能,内部可能需要调用多个服务并操作多个数据库实现,服务调用的分布式事务问题变的非常突出。
3)微服务数量众多,其测试、部署、监控等都变的更加困难。
随着RPC框架的成熟,第一个问题已经逐渐得到解决。例如dubbo可以支持多种通讯协议,springcloud可以非常好的支持restful调用。对于第三个问题,随着docker、devops技术的发展以及各公有云paas平台自动化运维工具的推出,微服务的测试、部署与运维会变得越来越容易。
而对于第二个问题,现在还没有通用方案很好的解决微服务产生的事务问题。分布式事务已经成为微服务落地最大的阻碍,也是最具挑战性的一个技术难题。
二、分布式基本理论
1、CAP理论
在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)3 个要素最多只能同时满足两个,不可兼得。其中,分区容错性又是不可或缺的。
-
一致性:分布式环境下多个节点的数据是否强一致。
-
可用性:分布式服务能一直保证可用状态。当用户发出一个请求后,服务能在有限时间内返回结果。
-
分区容错性:特指对网络分区的容错性。
举例:
默认优先选择AP,弱化C。Cassandra、Dynamo 等。
默认优先选择CP,弱化A。HBase、MongoDB ,Redis等作为数据库必须首要满足就是一致性,否则没有意义,所以宁可等待查询降低可用性,也不会返回错误数据。
默认优先选择AC,弱化C。相对于现在的电商,可用性相比强一致性更为重要,所以抢购场景中会出现明明有存货,点进去支付的时候又没有了,他们要求的是最终一致性。
2、BASE理论
核心思想:
-
基本可用(Basically Available):指分布式系统在出现故障时,允许损失部分的可用性来保证核心可用。
-
软状态(Soft State):指允许分布式系统存在中间状态,该中间状态不会影响到系统的整体可用性。
-
最终一致性(Eventual Consistency):指分布式系统中的所有副本数据经过一定时间后,最终能够达到一致的状态。
三、一致性理论
分布式事务的目的是保障分库数据一致性,而跨库事务会遇到各种不可控制的问题,如个别节点永久性宕机,像单机事务一样的ACID是无法奢望的。另外,业界著名的CAP理论也告诉我们,对分布式系统,需要将数据一致性和系统可用性、分区容忍性放在天平上一起考虑。
简单介绍下ACID,关系型数据库必备特性
(摘自*)
-
Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
-
Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚。
-
Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
-
Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
两阶段提交协议(简称2PC)是实现分布式事务较为经典的方案,但2PC 的可扩展性很差,在分布式架构下应用代价较大,eBay 架构师Dan Pritchett 提出了BASE 理论,用于解决大规模分布式系统下的数据一致性问题。BASE 理论告诉我们:可以通过放弃系统在每个时刻的强一致性来换取系统的可扩展性。
1、一致性模型
-
强一致性:数据更新成功后,任意时刻所有副本中的数据都是一致的,一般采用同步的方式实现。
-
弱一致性:数据更新成功后,系统不承诺立即可以读到最新写入的值,也不承诺具体多久之后可以读到。
-
最终一致性:弱一致性的一种形式,数据更新成功后,系统不承诺立即可以返回最新写入的值,但是保证最终会返回上一次更新操作的值。
四、分布式事物的解决方案
关于分布式事务,工程领域主要讨论的是强一致性和最终一致性的解决方案。典型方案包括:
-
两阶段提交(2PC, Two-phase Commit)方案
-
TCC 补偿模式
-
基于消息
-
其他
1、2PC方案——强一致性
2PC的核心原理是通过提交分阶段和记日志的方式,记录下事务提交所处的阶段状态,在组件宕机重启后,可通过日志恢复事务提交的阶段状态,并在这个状态节点重试,如Coordinator重启后,通过日志可以确定提交处于Prepare还是PrepareAll状态,若是前者,说明有节点可能没有Prepare成功,或所有节点Prepare成功但还没有下发Commit,状态恢复后给所有节点下发RollBack;若是PrepareAll状态,需要给所有节点下发Commit,数据库节点需要保证Commit幂等。
2PC方案的问题:
-
同步阻塞。
-
数据不一致。
-
单点问题。
升级的3PC方案旨在解决这些问题,主要有两个改进:
-
增加超时机制。
-
两阶段之间插入准备阶段。
但三阶段提交也存在一些缺陷,要彻底从协议层面避免数据不一致,可以采用Paxos或者Raft算法。2,3PC提交方案应用非常广泛,几乎所有商业OLTP数据库都支持XA协议。但是两阶段提交方案锁定资源时间长,对性能影响很大,而且XA提交对于业务相对透明,不利于开发者对业务的充分掌控。基本不适合解决微服务事务问题。
2、TCC (Try-Confirm-Cancel)补偿模式——最终一致性
TCC方案在电商、金融领域落地较多。TCC方案其实是两阶段提交的一种改进。其将整个业务逻辑的每个分支显式的分成了Try、Confirm、Cancel三个操作。Try部分完成业务的准备工作,confirm部分完成业务的提交,cancel部分完成事务的回滚。基本原理如下图所示。
事务开始时,业务应用会向事务协调器注册启动事务。之后业务应用会调用所有服务的try接口,完成一阶段准备。之后事务协调器会根据try接口返回情况,决定调用confirm接口或者cancel接口。如果接口调用失败,会进行重试。
TCC方案让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。 当然TCC方案也有不足之处,集中表现在以下两个方面:
-
对应用的侵入性强。业务逻辑的每个分支都需要实现try、confirm、cancel三个操作,应用侵入性较强,改造成本高。
-
实现难度较大。需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm和cancel接口必须实现幂等。
上述原因导致TCC方案大多被研发实力较强、有迫切需求的大公司所采用。微服务倡导服务的轻量化、易部署,而TCC方案中很多事务的处理逻辑需要应用自己编码实现,复杂且开发量大,但是相比XA阶段提交对于业务掌控度更高,这点在金融电商方面尤其重要。
3、基于消息——最终一致性
消息一致性方案是通过消息中间件保证上、下游应用数据操作的一致性。基本思路是将本地操作和发送消息放在一个事务中,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。
消息方案从本质上讲是将分布式事务转换为两个本地事务,然后依靠下游业务的重试机制达到最终一致性。基于消息的最终一致性方案对应用侵入性也很高,应用需要进行大量业务改造,成本较高。
4、其他
阿里的GTS,eBay的事件队列方案,腾讯的CMQ等,有兴趣的童鞋可以自己扩展下。
四、选择建议
在面临数据一致性问题的时候,首先要从业务需求的角度出发,确定我们对于3 种一致性模型的接受程度,再通过具体场景来决定解决方案。
从应用角度看,分布式事务的现实场景常常无法规避,在有能力给出其他解决方案前,2PC也是一个不错的选择。
对购物转账等电商和金融业务,中间件层的2PC最大问题在于业务不可见,一旦出现不可抗力或意想不到的一致性破坏,如数据节点永久性宕机,业务难以根据2PC的日志进行补偿。金融场景下,数据一致性是命根,业务需要对数据有百分之百的掌控力,建议使用TCC这类分布式事务模型,或基于消息队列的柔性事务框架,这两种方案都在业务层实现,业务开发者具有足够掌控力,可以结合SOA框架来架构,包括Dubbo、Spring Cloud等。
加我微信进技术交流群,各大教学资源免费领取,大厂内推!关注订阅号:一只可爱的小码农,不定时推送高质量学习文章。