IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 分布式系统数据一致性解决方案 -> 正文阅读

[Java知识库]分布式系统数据一致性解决方案

1.背景

微服务架构是一把双刃剑,我们在享受微服务拆分带来好处的同时,势必会遇到数据模型和服务之间不一致的问题。本文讨论的都是互联网企业在分布式架构下如何应对数据一致性问题的。

2.典型的一致性问题

案例1:下单和减库存的一致性

下单减库存有一致性的需求,如果下单了但是没减库存造成的结果是超卖;如果减库存了但下单失败会导致少卖。

案例2:缓存和数据库的数据一致性

电商系统一般会将热点数据缓存来减少数据库访问压力,这要求缓存和数据库数据是一致的,如果数据库数据发生变更但同步到缓存失败,这时就产生了缓存何数据库数据的不一致。

案例3:两系统协调的一致性

其实上面两个案例都可以归结为此案例,系统是上下游的关系,由于超时等原因可能导致两系统数据的状态不对。

3.一致性理论

ACID原理

即关系型数据库的事务特性:原子性、一致性、隔离性和持久性。ACID这里不多说,一般核心交易系统都要有强一致性的事务要求,因此在做技术选型时,核心数据基本也只考虑关系型数据库。

CAP原理

CAP其实是一种权衡平衡的思想,用于指导在系统可用性设计、数据一致性设计时做权衡取舍。CAP,即一致性、可用性、分区容忍性。一致性强调在同一时刻副本一致,可用性指的是服务在有限的时间内完成处理并响应,分区容忍性说的是分布式系统本身的特点可以独立运行并提供服务。

4.一致性协议

说到一致性协议,大家肯定都听过两阶段协议协议、三阶段提交协议、Paxos协议、Raft协议等。这里我们简单比较一下。

两阶段提交协议

两阶段提交协议,顾名思义分两个阶段:准备阶段(投票反馈阶段)和 提交阶段(执行阶段)。准备阶段参与者在本地执行事务,在本地写redo\undo log,但不提交,有点万事俱备只欠东风的态势。提交阶段由协调者发出。

两阶段协议提交存在的问题还挺多,具体包含:

  1. 阻塞问题,所有参与者必须收到协调者发起的指令才会提交,否则资源会被锁定,但由于不可靠的网络,协调者可能无法收到所有反馈,参与者也可能无法接收到指令。
  2. 单点故障,如果协调者宕机,参与者没有协调者指挥就会一致阻塞。
  3. 数据不一致,协调者发出的指令可能导致部分参与者收到,部分参与者由于网络原因未收到,有数据不一致的风险。

三阶段提交协议

针对两阶段提交的一些问题,三阶段提交做了一些改进,比如:

  1. 通过超时机制解决阻塞问题。
  2. 通过增加询问环节降低锁资源导致阻塞的概率。

三阶段即canCommit、preCommit、doCommit。三阶段提交解决了资源阻塞问题,但仍然存在单点故障和数据不一致的问题。

Raft投票协议

前面说的两阶段提交协议和三阶段提交协议都存在单点问题和数据不一致的风险。为此Raft、Paxos、Zab使用的机制都是大多数的投票机制来解决单点问题和消灭不一致风险。我们以Raft协议来说,它分两个过程:

  1. Leader选举。
  2. 日志复制。

首先,Leader选举是用于解决单点问题。一开始所有的节点都是追随者,当节点感知不到Leader存在时就会转变身份称为竞选者,为了防止出现多个竞选者竞争Leader的局面出现,每个节点都会等待一个随机时间才能称为竞选者。竞选者发起投票,当获得大多数同意反馈时则称为了Leader。Leader会定期对Follower发起心跳以通知自己的存活。通过心跳+投票机制防止了单点故障。

然后,我们看看Raft是如何解决不一致问题的,这里使用到一个单调递增的序列号,通过序列号 + 多数投票机制保证数据一致性。

Raft一致性原理可参考我原来写过的《全面理解Raft协议》一文。

TCC

TCC是互联网公司业务系统保证数据一致性的一种常见方法,它将任务拆分为Try、Confirm、Cancel三个过程,本质上Try、Confirm思想和两阶段是一致的,只是TCC增加了逆向过程,因此具有一定的修复能力。但TCC还是存在不一致的风险,面对这个问题,商业公司一般做法是保证最终一致,通过一些自动化补偿手段尽可能做到数据一致,且不用人工干预。

小结:两阶段提交协议是所有一致性协议的基础,它是一个强一致性协议,但可用性太差;三阶段提交协议改善了两阶段提交协议,但还是为解决单点问题和存在不一致的风险;可靠的、强一致性协议最后是通过Raft、Paxos等投票协议做成的。一致性协议都有一个共同的问题,就是实现复杂,性能不好,更多用于HA组件,而非业务系统本身。

5.最终一致性模式

业务系统对一致性大多数情况没那么高,更多强调高可用,因此,多数业务系统强调最终一致性,这其实也是在CAP权衡下倾向于A。我们本节来分析一下最终一致性方案。最终一致性都强调写操作失败后进行补偿补救,常见的最终一致性有4种形态,我们依次分析分析。

查询模式

对于任何写操作,API设计时都要提供对应的查询或批量查询接口。如下图所示。

如下订单操作

interface OrderService {
 boolean submitOrder(int ticketId, OrderEntity order);
 Order queryOrder(int ticketId);
}

重试+逆向模式

即当写操作失败了上游系统提供重试接口和逆向取消接口。上游系统需要保障重试写接口与逆向接口的幂等性。

举例:

interface OrderService {
 boolean submitOrder(int ticketId, OrderEntity order);
 boolean cancelOrder(int ticketId);
}

补救任务模式

写操作失败后,同步创建补救任务,通过定时执行补救任务来达到最终一致。如果补救任务是一个消息通知型任务,有时会将写操作和通知任务放在一个事务中来保证写操作成功通知任务一定成功,并最终能够执行。

异步消息模式

该模式下通过消息队列来保证最终一致性,为了保证消息的可靠性,可以在业务系统发送消息前将写操作和消息持久化做成事务,然后异步通过消息队列中间件发送消息。该模式可能会出现重复消息,因此消息消费方需要保证幂等性。

6.典型案例的一致性方案

上一节我们已经讲过互联网公司在CA中倾向了高可用,选择了高可用最终一致性。因此典型案例的一致性方案不会是使用一致性协议的强一致性方案。

案例1:下单和减库存的一致性

由于允许短暂的不一致,因此库存接口可提供扣减写接口、查询接口以及逆向增接口。

interface InventoryService {
 boolean deduct(int ticketId, int skuId);
 OpLog queryDeductLog(int ticketId);
 boolean cancelDeduct(int ticketId, int skuId);
}

客户端可通过“重试 + 逆向”模式同步方式进行一致性保证,也可以走异步补救任务或异步消息进行补救。

电商平台还会采用一种更加严格的一致性手段,扣减库存分两个阶段:锁库存、确认减库存。为了保证失败后能够回滚还会解锁库存。扣减库存其实思想和两阶段提交是一样的,客户端其实就是协调者角色,客户端的可用性由系统监控告警和运维人员保障,参与者就是服务端,这里只有一个参与者。当然为了保证最终一致性还会有重试、逆向回滚以及异步补偿任务、异步消息等方式。

案例2:缓存和数据库的数据一致性

缓存是为了应对高并发的读请求,但当数据发生变更是需要将变化同步到缓存。

首先,我们这里并不探讨强一致,因此在高并发时很短暂的不一致是可以接受的。因此先写数据库成功,但写缓存还未完成而操作从缓存读到旧数据这种情况是可以接受的。

其次,我们简单说下是淘汰缓存还是更新缓存的问题,在两线程并发修改同一对象写入顺序不确认,采用更新缓存方式问题其实很多,因此我们就不自添烦恼了,淘汰缓存虽然可能造成一次mismatch,但其实还好。因此我们的结论是淘汰缓存。

最后,我们探讨一次写数据库和缓存的先后顺序问题。由于写数据库和写数据库有一个先后顺序,这样就可能导致前一个写操作成功而后一个写操作失败的情况出现,不管是先写数据库先还是写缓存先,这种情况都会出现长时间不一致性的情况出现,因此需要处理这种异常情况。

  1. 淘汰缓存成功、写数据库失败

其实这时一个伪问题,我们更新数据当然希望持久生效,所以如果先淘汰缓存,写数据库失败当然需要千方百计保证写数据库成功,如果网络本省就不可用怎么写都失败,这样就违背我们的初衷,因此很显然是必须先操作数据库而不是缓存。

2.写数据库成功、淘汰缓存失败

如果操作数据库成功,淘汰缓存失败导致缓存和数据库不一致,因此可以通过我们前面提到的补救措施,比如重试、补救任务、异步消息来保证最终一致性。关键还是变更的数据持久生效!!!

小结:针对缓存和数据库一致性的策略是:先写数据库,再淘汰缓存。缓存和数据库的不一致可通过最终一致性模式解决。如果强一致性有要求,可将写数据库和淘汰缓存做到一个事务中,当淘汰缓存失败则回滚数据库变更,这样缓存和数据库数据还是一致的。

总结

本文较全面的分析了分布式系统的数据一致性模式。我们首先介绍了两个重要的原理:ACID和CAP,理解这两个原理对于理解事务和分布式事务很重要,读者朋友细细体会以下ACID的事务特征和CAP中CA的权衡思想。文章介绍了强一致性协议P:两阶段协议、三阶段协议和典型的投票协议。分布式架构下理解CAP很重要,目前互联网公司倾向于高可用最终一致性方案,本文介绍了4种常用的最终一致性模式,文章最后针对“下单和减库存”以及“缓存和数据库一致性”给出了解决方案,希望读者朋友看完之后有帮助。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-04 15:22:29  更:2022-03-04 15:22:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 12:10:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码