目录

1. 从数据一致性谈起

1.1 数据一致性的情形

1.2 数据一致性的概念

1.3 数据一致性的原理

1.4 数据一致性的协议

2. 分布式服务间的数据一致性

3. 分布式事务一致性解决方案

3.1 接口同步调用模式与一致性解决方案

3.2 接口异步调用模式与一致性解决方案

3.3 消息异步处理模式与一致性解决方案

4. 保证操作幂等性的常用方法

5. 总结


1. 从数据一致性谈起

  一致性问题,“万恶之源”是数据冗余和分布并通过网络交互+网络异常是常态。

1.1 数据一致性的情形

  • 主库、从库和缓存数据一致性,相同数据冗余,关系数据库,为保证关据库的高可用和高性能,一般会采用主从(备)架构并引入缓存。其中数据不一致性存在于数据冗余的时间窗口内。常用的解决方案见数据库之架构
  • 多副本数据之间的数据一致性,相同数据副本,大数据领域,一份数据会有多个副本并存储到不同的节点上。客户端可以访问任何一个节点进行读写操作。常用的解决方案是基于Paxos、ZAB、Raft、Quorum、Gossip等的开源实现。这里只是一提,暂不探讨。感兴趣可以自行谷歌或百度。
  • 分布式服务之间的数据一致性,相关数据分布,分布式服务,不同的服务操作不同的库(表),而且库(表)间要保持一致。常用的解决方案是分布式事务一致性解决方案。这也是本文要探讨的内容。

1.2 数据一致性的概念

  • 强一致性
  • 弱一致性
  • 最终一致性

1.3 数据一致性的原理

  • ACID原则
  • CAP原理
  • BASE原理

1.4 数据一致性的协议

  • 两阶段提交协议
  • 三阶段提交协议
  • TCC协议
  • Paxos协议
  • ZAB协议
  • Raft协议
  • Quorum协议
  • Gossip协议

2. 分布式服务间的数据一致性

  所谓分布式服务,就是把之前通过本地接口交互的模块,拆分成单独的应用独立部署,并通过远程接口和网络消息交互。且不管这样说严不严密,正不正确,理解就好。本文的重点也不是这个话题。简单画一张图辅助理解,如图。集中式架构,要想保证订单表和库存表的一致性,只要一个本地事务(ACID)就能保证两者的强一致性。分布式架构,订单表由订单服务操作,库存表由库存服务操作。要想保证订单表和库存表的一致性,那么就必须保证订单服务对订单表的操作和库存服务对库存表的操作同事成功。之前的一个本地事务就变成了一个分布式事务。由于服务之间通过网络交互+网络异常是常态,就会产生服务间数据不一致的情况。这就涉及一个分布式事务一致性的问题。

3. 分布式事务一致性解决方案

3.1 接口同步调用模式与一致性解决方案

模式分析:A服务同步调用B服务的接口并等待结果返回,后续的流程会依赖B服务的返回结果。这种交互模式下,A服务得到的结果细分有三种情况。

  1. 请求发起阶段网络超时或异常,此时,B服务未收到请求,未作出相应的处理;
  2. 结果返回阶段网络超时或异常,此时,B服务已收到请求,并作出相应的处理;
  3. 正常结果返回(明确的成功或失败)。

业务场景:适用于大规模、高并发的短小操作且依赖返回值的场景。例如,交易服务和库存服务(卡券服务、红包服务等)的交互、用户登录和准入服务的交互等。

解决方案:方案一,服务调用方查询重试方案;

方案二,TCC方案。

1. 服务调用方查询重试方案,适合一个从业务服务场景。

1 下单减库存方法() {2     // 1.准备操作3     // 2.重试调用B服务4     result = RetryUtil {5         while(重试次数 < 最大重试次数) {6             try {7                 if (重试次数 != 0) {8                     // case1:网络超时或异常(catch分支)9                     // case2:查询到扣减库存操作,result=成功(return)
10                     // case3:查不到扣减库存操作,result=失败(继续下面操作)
11                     result = rpc.查询扣减库存是否成功();
12                     if (result == 成功) {
13                         return result;
14                     }
15                 }
16                 // case1:网络超时或异常(catch分支)
17                 // case2:扣减库存成功,result=成功(return)
18                 // case3:扣减库存失败,result=失败(return)
19                 return rpc.扣减库存();
20             } catch (Exception e) {
21                 if (重试次数 = 最大重试次数) {
22                     // 报警,人工处理或者(近实时)对账系统自动校准
23                     // 抛出异常,中断后续流程
24                     throw 自定义异常; //或者result封装异常
25                 }
26             }
27         }
28     };
29     // 3.后续操作
30 }

:1) 查询重试后依然失败(极少),报警,人工处理或者准实时对账系统自动校准;

2) 重试次数不宜多,甚至只重试一次;

3) B服务处理请求要做幂等。

2. TCC方案,适合多个从业务服务场景。TCC是阿里在二阶段提交协议的基础上提出的一种解决分布式事务一致性的协议,原理图如下。其对应的产品是DTX(老版是DTS)。DTS中有个快速开始的例子看明白了,TCC就基本OK了。在蚂蚁金服内部被广泛地应用于交易、转账、红包等核心资金链路,服务于亿级用户的资金操作。

:关于TCC,个人认为,理解原理很重要。工作中遇到吻合的场景可以根据原理自行实现,满足业务即可。

3.2 接口异步调用模式与一致性解决方案

模式分析:A服务调用B服务,B服务先受理请求并落库,状态是待处理。B服务处理请求很耗时,或者要依赖其他的服务。B服务处理完后通知A服务或者A服务定时去查询B服务的处理结果。这种交互模式下,对于CASE-1,第1步和第2步同接口同步调用模式,第3步同消息异步处理模式;对于CASE-2,相当于两次接口同步调用模式

业务场景:适用于非核心链路上负载较高的处理环节,这个环节经常耗时较长,并且对时效性要求不高。例如,用户提现时,账户系统和提现系统的交互(CASE-1);提现系统和三方系统(银行系统或者三方托管系统)的交互(CASE-2)。

解决方案服务被调方最大努力处理方案。由于B服务中请求有落库,所以可以用定时任务不断重试尽最大努力将请求处理出结果。处理后,将请求状态设置成对应的结果落库。然后再通知A服务或者A服务异步主动查询。

1 受理请求方法() {2     // 1.请求落库,状态为待处理3     // 2.返回受理结果4     if (落库成功) {5         // 返回受理成功6     } else {7         // 返回受理失败8     }9 }
10
11 定时任务处理请求方法() {
12     // 1.扫描待处理请求
13     try {
14         // 2.处理请求
15         if (处理成功) {
16             // 设置请求处理状态为处理成功
17         } eles {
18             //  设置请求处理状态为处理失败
19         }
20     } catch (Exception e) {
21         // 不做任何操作,请求状态依旧为待处理
22     }
23     // 3.消息通知A服务处理结果或者等待A服务查询处理结果
24 }

:1) B服务通常都是接受请求并持久化后才返回A服务受理成功。避免服务进程被杀掉而导致请求丢失。

  2) 不管是第(1,2)两步还是CASE-2中的第(3,4)两步,如果查询重试失败,可以落库,用定时任务处理,知道成功。反正不像接口同步调用模式,A服务不需要实时的结果。

3.3 消息异步处理模式与一致性解决方案

模式分析:A服务将B服务需要的信息通过消息中间件传递给B服务,A服务无需知道B服务的处理结果。这种交互模式下,消息生产者要确保消息发送成功;消息消费者要确保消息消费成功。

业务场景:消息异步处理模式与接口异步调用模式类似,多应用于非核心链路上负载较高的处理环节中,井且服务的上游不关心下游的处理结果,下游也不需要向上游返回处理结果。例如,在电商系统中,用户下订单支付且交易成功后,发送消息给物流系统或者账务系统进行后续的处理。

解决方案生产者最大努力通知+消费者最大努力处理方案。

  1. 非事务消息,生产者先执行本地事务并将消息落库,状态标记为待发送,然后发送消息。如果发送成功,则将消息改为发送成功。定时任务定时从数据库捞取在一定时间内待发送的消息并将消息发送。通过定时任务来保证消息的发送。为确保消息一定能消费,消费者一般采用手动ACK机制,那么消息服务器必然会重发未ACK的消息,这就要求消息消费者做好幂等。

     1 交易完成发消息方法() {2     // 1.设置交易状态为已完成3     // 2.消息落库,状态为待发送4     // 可异步发送,也建议异步发送5     try {6         // 3.发送消息7         if (发送成功) {8             // 设置消息状态为发送成功9         }
    10     } catch (Exception e) {
    11         // 不做任何操作,消息状态依旧为待发送
    12     }
    13 }
    14
    15 定时任务发送消息方法() {
    16     // 1.扫描待发送消息
    17     try {
    18         // 2.发送消息
    19         if (发送成功) {
    20             // 设置消息状态为发送成功
    21         }
    22     } catch (Exception e) {
    23         // 不做任何操作,消息状态依旧为待发送
    24     }
    25 }
  2. 事务消息,以RocketMQ为例,下图是RocketMQ事务消息的流程。官网有示例代码。和不支持事务的消息中间相比,只是消息发送的时候,保证了和本地事务的一致。消费者实现还是不变。

:1) 定时任务重试发送消息和消息服务器重发未ACK的消息一般都是时间阶梯式的(2n*时间间隔);

  2) 支持事务消息中间件之RocketMQ:Quick Start - Apache RocketMQ。

4. 保证操作幂等性的常用方法

  1. 有业务状态,业务逻辑来保证幂等。比如接到支付成功的消息订单状态变成支付完成,如果当前状态是支付完成,则再收到一个支付成功的消息则说明消息重复了,直接作为消息成功处理。
  2. 无业务状态,业务唯一ID保证幂等。增加一个去重表(或分布式缓存)来记录有业务唯一ID的操作。比如调用充值接口,当请求过来时,会根据唯一充值ID去查充值流水表,若已经存在,则直接返回;否则继续进行充值操作。

:保证幂等性的方法很多,根据具体的业务场景,总能很容易找到保证幂等性的方法。

5. 总结

  1. 接口同步调用模式,服务调用方查询重试方案和TCC方案。
  2. 接口异步调用模式,服务被调方最大努力处理方案。
  3. 消息异步处理模式,生产者最大努力通知+消费者最大努力处理方案。
  4. 任何服务操作都需要提供一个查询接口,用来向外部输出操作执行的状态。
  5. 永远不要在本地事务中调用远程服务,在这种场景下如果远程服务出现了问题,则会拖长事务,导致应用服务器占用太多的数据库连接,让服务器负载迅速攀升,在严重情况下会压垮数据库。
  6. 最后一道防线 - 对账系统。
  7. 同步和异步的抉择:
    • 可以异步的地方,就应该异步实现。如果业务逻辑允许,则我们可以将一些耗时较长的、用户对响应没有特别要求的操作异步化,以此来减少核心链路的层级,释放系统的压力。
    • 能用同步解决的问题,不要引入异步。如果性能不是问题,或者所处理的操作是短小的轻量级处理逻辑,那么同步调用方式是最理想不过的,因为这样不需要引入异步化的复杂处理流程。
  8. 最后,来思考下,垂直分库情况的数据一致性问题,相信你已经有答案了。

注:如果,以上场景和解决方案,没能包含您工作中遇到的场景,欢迎交流,并共同讨论解决方案。

分布式事务数据一致性解决方案(一)相关推荐

  1. 分布式事务数据一致性解决方案

    Base理论 Basically Available 基本可用 Soft state 软状态 Eventually consistent 最终一致性 三个短语的 缩写,通过牺牲强一致性来获取最终一致性 ...

  2. 分布式事务的解决方案

    数据库事务 在说分布式事务之前,我们先从数据库事务说起. 数据库事务可能大家都很熟悉,在开发过程中也会经常使用到.但是即使如此,可能对于一些细节问题,很多人仍然不清楚.比如很多人都知道数据库事务的几个 ...

  3. 分布式事务一致性解决方案

    一.从数据一致性谈起↑ 一致性问题,"万恶之源"是数据冗余和分布并通过网络交互+网络异常是常态. 1.数据一致性的情形 主库.从库和缓存数据一致性,相同数据冗余,关系数据库,为保证 ...

  4. 这些分布式事务的解决方案,你都知道吗

    转载自   这些分布式事务的解决方案,你都知道吗 分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在微服务架构中,几乎可以说是无法避免. 数据库事务 在说分 ...

  5. springcloud分布式事务解决方案_搞懂分布式技术18:分布式事务常用解决方案

    本文转载自 http://linkedkeeper.com 本文内容参考网络,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 http ...

  6. 分布式事务及解决方案

    分布式事务 1 微服务化带来的分布式事务问题 开发当中真实场景: 首先,设想一个传统的单体应用(Monolithic App),通过 3 个 Module,在同一个数据源上更新数据来完成一项业务. 很 ...

  7. cap理论与分布式事务的解决方案

    现在很火的微服务架构所设计的系统是分布式系统.分布式系统有一个著名的CAP理论,即一个分布式系统要同时满足一致性(Consistency).可用性(Availablility)和分区容错(Partit ...

  8. 分布式事务终极解决方案探讨

    2019独角兽企业重金招聘Python工程师标准>>> 分布式事务终极解决方案探讨 转载于:https://my.oschina.net/dslcode/blog/1606115

  9. 谈谈分布式的场景及分布式事务的解决方案

    一.解决java集群的session共享的解决方案: 1.客户端cookie加密.(一般用于内网中企业级的系统中,要求用户浏览器端的cookie不能禁用,禁用的话,该方案会失效). 2.集群中,各个应 ...

最新文章

  1. Asp.NET中常用的一些优化性能的方法
  2. Matlab与数据结构 -- 搜索向量或矩阵中非零元素的位置
  3. 程序升级触发阿里云自身安全机制导致的莫名问题
  4. 只腐蚀毛刺 腐蚀算法_摩托车油箱防腐蚀、油封安装、密封清洗经验分享
  5. HTTP长连接短连接
  6. 细谈C语言中的strcpy,strncpy,memcpy,memmove,memset函数
  7. Py之SnowNLP:SnowNLP中文处理包的简介、安装、使用方法、代码实现之详细攻略
  8. 工具-eclipse-hibernate关于快速生成*.hbm.xml文件,与bean文件的操作,不使用hibernate tool解决办法之一,主要为了快速开发,写的小工具
  9. Leetcode题库217.存在重复元素(python实现)
  10. 记录一个AndroidX和Android support库不能共存的坑
  11. 当Terraform遇上ECS(一)——DataSource篇
  12. 北交所开市工作准备就绪 定于2021年11月15日开市
  13. Mac设置多屏幕的时候程序坞的位置
  14. python蟒蛇绘制
  15. Junit单元测试/反射/注解
  16. 中文停用词文档_实战:朴素贝叶斯对文档进行分类
  17. Arcpy 实现批量按掩膜提取
  18. ArcGIS API for JavaScript 打印
  19. wlan 网速测试软件,WiFi大师网速测试
  20. [互联网思维(转)]互联网思维法则

热门文章

  1. 透过现象看本质: 常见的前端架构风格和案例
  2. 一文了解 nostr :一个让 Elon Musk 感到害怕的去中心化社交协议
  3. android 的Android Media Scanner多媒体扫描路径问题
  4. java flexpaper_Java+FlexPaper+swfTools 文档在线预览demo
  5. Pg处理MIMIC数据
  6. 教女朋友学会用win10+yolov3+python训练自己的模型
  7. [转]战地3寒霜2引擎详解:物件光照效果技术特性
  8. Fragment中添加toolbar-menu
  9. WPF聚光灯光源学习
  10. Python GUI编程:制作一个文档图片提取软件