分布式系统:数据一致性解决方案

2024-09-06 20:18

本文主要是介绍分布式系统:数据一致性解决方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

点击上方蓝色字体,选择“设为星标

回复”资源“获取更多资源

大数据技术与架构

点击右侧关注,大数据开发领域最强公众号!

大数据真好玩

点击右侧关注,大数据真好玩!

在分布式系统中,随着系统架构演进,原来的原子性操作会随着系统拆分而无法保障原子性从而产生一致性问题,但业务实际又需要保障一致性,下面我从学习和实战运用总结一下分布式一致性解决方案。

1. CAP & Base理论



CAP定理指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。这三个要素最多只能同时实现两点,不可能三者兼顾:

  • 一致性:在分布式系统中的所有数据备份,在同一时刻是否同样的值。

  • 分区容错性:可靠性,无论应用程序或系统发生错误,还是用户以意外或错误的方式使用,软件系统都能继续运行。

  • 可用性:在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。





CAP理论3选2是伪命题,实际上必须从A和C选择一个和P组合,更进一步基本上都会选择A,相比一致性,系统一旦不可用或不可靠都可能会造成整个站点崩溃,所以一般都会选择AP。但是不一致的问题也不能忽略,使用最终一致是比较好的办法。



BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。接下来看一下BASE中的三要素:

  • 基本可用(Basically Available):基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性。注意,这绝不等价于系统不可用。比如:

    • 响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒

    • 系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。

  • 软状态(Soft State):软状态指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。

  • 最终一致(Eventually Consistent):最终一致性强调的是所有的数据副本,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。



最终一致的核心做法就是通过记录对应操作并在操作失败时不断进行重试直到成功为止。

2. 重试

在出现一致性问题时如果系统的并发或不一致情况较少,可以先使用重试来解决。



在调用Service B超时或失败时进行重试:

  • 同步调用,捕获异常重新调用Service B。

  • 异步消息,捕获异常发送延迟消息重新调用Service B。

  • 异步线程,捕获异常开启异步线程重新调用Service B。



如果重试还是不能解决问题,那么需要使用分布式事务来解决。

3. 分布式事务

对于分布式一致性问题可以采用分布式事务来解决。

3.1 2PC-XA协议

XA事务由一个或多个资源管理器(Resource Managers)、一个事务管理器(Transaction Manager)以及一个应用程序(Application Program)组成。

  • 资源管理器(RM):参与者。提供访问事务资源的方法。通常一个数据库就是一个资源管理器。

  • 事务管理器(TM):协调者。分配标识符,监视事务的进度,并负责事务完成和故障恢复。

  • 应用程序(AP):发起者。定义事务的边界,制定全局事务中的操作。



整个过程分为2个阶段:准备(prepare)和提交(commit),prepare前需要先执行对应的DML操作。





Mysql XA事务语句:

注:执行前需要先生成全局唯一的xidXA {START|BEGIN} xid [JOIN|RESUME]
DML操作
XA END xid [SUSPEND [FOR MIGRATE]]
XA PREPARE xid //1.准备
XA COMMIT xid [ONE PHASE] //2.提交
XA ROLLBACK xid
XA RECOVER

简单的事例:

//准备前阶段
mysql> XA START 'xatest';
Query OK, 0 rows affected (0.00 sec)mysql> INSERT INTO mytable (i) VALUES(10);
Query OK, 1 row affected (0.04 sec)mysql> XA END 'xatest';
Query OK, 0 rows affected (0.00 sec)
//准备
mysql> XA PREPARE 'xatest';
Query OK, 0 rows affected (0.00 sec)
//提交
mysql> XA COMMIT 'xatest';
Query OK, 0 rows affected (0.00 sec)

XA看起来很美好,但还是存在一些问题:

  • commit丢失/超时导致数据不一致。A commit成功,B commit时网络超时导致数据库未收到commit请求。

  • 性能低。所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态。

  • 主备数据不一致。

  • 协调者单点故障,在任意阶段协调者发生故障都会导致分布式事务无法进行。



3.2 3PC

3PC通过在参与者加入超时机制解决2PC中的协调者单点带来的事务无法进行的问题,但是性能和一致性仍没有解决。



3.3 TCC



这样看下来强一致是很难做了,还是最终一致把。。。



TCC是通过最终一致来达到分布式事务的效果,即:在短时间内无法保证一致性,但最终会一致。核心分为2个阶段:1.Try  2.Confirm or Cancel。先尝试(Try)操作数据,如果都成功则全部确认(Confirm)该修改,如果有任意一个尝试失败,则全部取消(Cancel)。简单点说就是增加了中间态和可回改能力。





通过重试机制保障Confirm和Cancel一定能成功。TCC解决了性能问题,但是业务系统想要实现对业务代码的侵入性很大,可以学习一下阿里GTS是如何做的。



3.4 事务管理器

在实际运用中事务管理器(TM)一般是由应用程序(AP)兼职实现的,这样对业务代码侵入性大,最好是把TM做成中间层。接下来详细看一下TM的职责:协调者。分配标识符,监视事务的进度,并负责事务完成和故障恢复。TM需要具备持久化能力才能完成监控、故障恢复和协调,整个协调过程可以分为3个阶段:

  • 准备阶段:向TM注册全局事务并获取到全局唯一XID,这一步需要定义好事务的范围。

  • 执行阶段:应用程序AP执行业务功能操作自己的RM,全部执行完毕后向TM commit,如果无失败则整个事务成功。

  • 确认/回滚阶段:如果第2步出现异常会触发该阶段,TM咨询各个AP对应操作是否成功,如果成功则commit,如果失败则调用AP进行rollback。



下面简单介绍几种实现方式。

3.4.1 本地事务管理器

对于简单的业务可能只要保障2个数据库之间的一致,这样在本地实现事务管理器比较快成本也不高。





  1. 1. 在做业务逻辑之前把对应事件添加到本地event表中(记录订正时所需要的关键数据)

  2. 2. 执行业务逻辑

  3. 1.本地业务逻辑,操作表数据等等

  4. 2.RPC调用其他服务

  5. 3.修改event状态为确认

  6. 4. 如果第2步不成功,则event状态还是预提交,通过定时任务捞取再执行订正逻辑

  7. 5. 检查业务逻辑中的操作是否完成,如未完成则执行订正逻辑

  8. 1.本地业务逻辑是否执行成功

  9. 2.RPC调用其他服务是否执行成功

  10. 6.返回订正结果

  11. 1.成功则修改event为确认提交

  12. 2.未成功则不操作,等待轮询订正

3.4.2 外部事务管理器

阿里GTS:https://help.aliyun.com/document_detail/157850.html

3.5 DB和MQ之间的一致性



在实际场景中,业务系统对本地DB数据变更后会广播对应的消息,消费者消费消息做自己的业务逻辑,按正常逻辑消息会在数据库变更后发出,如果消息发送超时且失败那么DB和MQ之间就产生了不一致问题,如何解决呢?使用可靠消息来解决,核心逻辑保证消息从投递到消费的过程中不会丢失:生产者confirm、消费者ack和持久化。理论上只要使用生产者确认机制即可,但是不使用消费者ack则没有意义。消费者需要开启手动ack同时做到幂等消费,MQ需要通过将exchange、queue和message进行持久化来保证消息不丢失,生产者则需要通过确认机制保证消息一定投递到MQ种,接下来重点讲解一下生产者确认机制。



confirm机制是在消息投递到所有匹配的queue之后发送确认消息给生产者,这样生产者就知道消息投递成功,但是由于消息是在DB操作之后发出的,生产者必须增加记录表来记录消息投递状态,如果投递成功就在收到确认消息时把记录标记为投递成功,如果长时间未收到确认消息则大概率是消息丢失了,再定时重新投递,这样就可以保证消息最终一定能投递成功。核心逻辑其实就是通过全局事务ID来标识DB操作和MQ消息是在一个分布式事务中的。



疑点:在学习RabbitMQ confirm机制时发现生产者接收到的确认消息只有消息ID,这个消息ID不能作为全局事务ID,所以无法解决DB和MQ之间的一致性问题,后续再看看其它MQ产品是怎么做的。



上面的方式需要业务系统维护消息状态,这部分可以交给中间件来实现,实现逻辑会变得不一样。





预投递的消息不会分发到queue中,只有在接收到确认投递的请求后才会进行投递,如果确认操作因为网络异常失败了,MQ在过一段时间之后主动询问业务系统该消息是否可投递(失败不断重试),这样就能在异常时做到最终一致,不过依赖MQ产品的能力。

3.6 DB和缓存之间的一致性

DB和缓存之间同样也存在不一致问题,先写DB再写缓存如果缓存写失败就不一致了,同样的需要重试更新缓存来做最终一致。方法可以参考“2.重试”部分,如果一定要保证重试不丢失可以用可靠消息或本地task表来记录重试操作,有条件的可以使用DB DRC消息对业务的侵入会小一些。需要注意的是同步更新和异步更新同时使用时可能会产生更新覆盖的问题,加上毫秒级的时间戳或版本号来丢弃旧的更新。

4. 兜底核对

虽然有了分布式事务,但是在实际场景中可能会因为bug导致数据不一致,这时需要兜底来做最后一道防线,通过定时核对数据是否一致,如不一致手动/自动进行订正。

4.1 系统自核对

如果系统数量和数据量不多的情况下可以由业务系统自行核对,通过发送延迟消息自消费或监听其他系统消息做相关数据核对并进行订正。



4.2 搭建核对系统

在核对工作繁多的情况下,由业务系统自己核对会存在很多耦合,这时可以选择搭建独立的核对系统进行核对和订正。



1.监听drc消息





2.监听业务消息



参考:

  • 阿里GTS:https://help.aliyun.com/document_detail/157850.html?spm=a2c4g.11186623.6.554.47ae4df4Zy6hBI

  • 饿了么DRC:https://zhuanlan.zhihu.com/p/34958596

  • Mysql XA:https://dev.mysql.com/doc/refman/8.0/en/xa-statements.html

版权声明:

本文为大数据技术与架构整理,原作者独家授权。未经原作者允许转载追究侵权责任。

编辑|冷眼丶

微信公众号|import_bigdata

欢迎点赞+收藏+转发朋友圈素质三连

文章不错?点个【在看】吧! ????

这篇关于分布式系统:数据一致性解决方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1143019

相关文章

MySQL 删除数据详解(最新整理)

《MySQL删除数据详解(最新整理)》:本文主要介绍MySQL删除数据的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、前言二、mysql 中的三种删除方式1.DELETE语句✅ 基本语法: 示例:2.TRUNCATE语句✅ 基本语

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

python实现对数据公钥加密与私钥解密

《python实现对数据公钥加密与私钥解密》这篇文章主要为大家详细介绍了如何使用python实现对数据公钥加密与私钥解密,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录公钥私钥的生成使用公钥加密使用私钥解密公钥私钥的生成这一部分,使用python生成公钥与私钥,然后保存在两个文

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

Navicat数据表的数据添加,删除及使用sql完成数据的添加过程

《Navicat数据表的数据添加,删除及使用sql完成数据的添加过程》:本文主要介绍Navicat数据表的数据添加,删除及使用sql完成数据的添加过程,具有很好的参考价值,希望对大家有所帮助,如有... 目录Navicat数据表数据添加,删除及使用sql完成数据添加选中操作的表则出现如下界面,查看左下角从左

SpringBoot中4种数据水平分片策略

《SpringBoot中4种数据水平分片策略》数据水平分片作为一种水平扩展策略,通过将数据分散到多个物理节点上,有效解决了存储容量和性能瓶颈问题,下面小编就来和大家分享4种数据分片策略吧... 目录一、前言二、哈希分片2.1 原理2.2 SpringBoot实现2.3 优缺点分析2.4 适用场景三、范围分片

Redis分片集群、数据读写规则问题小结

《Redis分片集群、数据读写规则问题小结》本文介绍了Redis分片集群的原理,通过数据分片和哈希槽机制解决单机内存限制与写瓶颈问题,实现分布式存储和高并发处理,但存在通信开销大、维护复杂及对事务支持... 目录一、分片集群解android决的问题二、分片集群图解 分片集群特征如何解决的上述问题?(与哨兵模

浅析如何保证MySQL与Redis数据一致性

《浅析如何保证MySQL与Redis数据一致性》在互联网应用中,MySQL作为持久化存储引擎,Redis作为高性能缓存层,两者的组合能有效提升系统性能,下面我们来看看如何保证两者的数据一致性吧... 目录一、数据不一致性的根源1.1 典型不一致场景1.2 关键矛盾点二、一致性保障策略2.1 基础策略:更新数