热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

双重写入:如何解决微服务分布式系统中数据不一致?Thorben

由于许多新应用程序是作为微服务系统构建的,因此双重写入已成为一个普遍的问题。它们是导致数据不一致的最常见原因之一。更糟的是,许多开发人员甚至都不知道双重写入是什么。什么是双重写入?



由于许多新应用程序是作为微服务系统构建的,因此双重写入已成为一个普遍的问题。它们是导致数据不一致的最常见原因之一。更糟的是,许多开发人员甚至都不知道双重写入是什么。

什么是双重写入?
双重写入描述了您在两个个系统(例如数据库和Apache Kafka)中更改数据时的情况,而没有额外的层来确保两个服务上的数据一致性。


只要两个操作都成功,一切就OK了。即使第一笔交易失败,也还是可以的。但是,如果您成功提交了第一笔交易而第二笔交易失败,则说明您遇到了问题。您的系统现在处于不一致状态,没有容易修复的方法。

分布式事务不再是一种选择
过去,当我们构建整体/.单体/Monolith时,我们使用分布式事务来避免这种情况。分布式事务使用两阶段提交协议。它将事务的提交过程分为两个步骤,并确保所有系统的ACID原则。
但是,如果我们要构建微服务系统,则不会使用分布式事务。这些交易需要锁,并且无法很好地扩展。他们还需要所有涉及的系统同时启动和运行。

那你该怎么办呢?

3个无效的“解决方案”
当我在会议演讲中或在一个研讨会上与与会者讨论此主题时,我经常听到以下3条建议之一:

  1. 是的,我们知道此问题,我们没有解决方案。但这还不错。到目前为止,什么都没有发生。让我们保持原样。
  2. 让我们将与Apache Kafka的交互移动到提交后监听器。
  3. 在提交数据库事务之前,让我们将事件写入Kafka中的主题。

好吧,很明显,建议1的风险很大。它可能在大多数时间都有效。但是迟早,您将在服务存储的数据之间创建越来越多的不一致之处。
因此,让我们集中讨论选项2和3。

2. 在提交后监听器中发布事件
在提交后监听器中发布事件是一种非常流行的方法。它确保仅在数据库事务成功时才发布事件。但是,很难解决Kafka崩溃或任何其他原因阻止您发布事件的情况。
您已经提交了数据库事务。因此,您无法轻松地还原这些更改。当您尝试在Kafka中发布事件时,其他事务可能已经使用并修改了该数据。
您可能会尝试将故障保留在数据库中,并运行常规的清理作业以尝试恢复失败的事件。这可能看起来像是一个合理的解决方案,但是它有一些缺陷:
  1. 仅当您可以将失败的事件保留在数据库中时,它才有效。如果数据库事务失败,或者您的应用程序或数据库崩溃,然后才能存储有关失败事件的信息,则将丢失它。
  2. 仅当事件本身没有引起问题时,它才起作用。
  3. 如果在清除作业恢复失败的事件之前,另一个操作为该业务对象创建了一个事件,则事件将混乱。

这些似乎是假设的情况,但这就是我们正在准备的事情。本地事务,分布式事务和确保最终一致性的方法的主要思想是,绝对要确保您不会造成任何(永久)不一致。
提交后侦听器无法确保这一点。因此,让我们看一下其他选项。

3. 在提交数据库事务之前发布事件
在讨论了为何提交后监听器不起作用之后,通常会建议使用这种方法。如果在提交之后发布事件会导致问题,您只需在提交事务之前发布它,对吗?
好吧,不……让我解释一下……
如果您无法发布事件,则在提交事务之前发布事件使您可以回滚事务。那就对了。
但是,如果数据库事务失败,该怎么办?
您的操作可能违反唯一约束,或者同一数据库记录上可能有2个并发更新。提交期间将检查所有数据库约束,并且不能确保它们都不会失败。数据库事务也彼此隔离,因此如果不使用锁,就无法阻止并发更新。但这带来了新的可伸缩性问题。简而言之,您的数据库事务可能会失败,并且您无能为力,或者对此无能为力。
如果发生这种情况,则您的活动已经发布。其他微服务可能已经观察到它并触发了一些业务逻辑。您无法撤回活动。
如前所述,撤消操作失败的原因相同。您也许可以构建一个大多数情况下都可以使用的解决方案。但是您无法创建绝对安全的东西。

如何避免双重写入?
您可以选择几种方法来避免双重写入。但是您需要知道,如果不使用分布式事务,则只能构建最终一致的系统。
总体思路是将流程分为多个步骤。这些步骤中的每个步骤仅适用于一个数据存储,例如数据库或Apache Kafka。这使您能够使用本地事务,所涉及系统之间的异步通信以及异步的,可能无限的重试机制。
如果您只想在服务之间复制数据或通知其他服务已发生事件,则可以将发件箱模式与Debezium等变更数据捕获实现一起使用。我在以下文章中详细解释了这种方法:


  • 使用Hibernate实现发件箱模式
  • 使用Debezium用CDC实现发件箱模式

而且,如果您需要实施涉及多个服务的一致的写入操作,则可以使用SAGA模式。我将在以下文章之一中对其进行详细说明。

结论
双重写入常常被低估,许多开发人员甚至都不知道潜在的数据不一致。
如本文所述,在没有分布式事务或确保最终一致性的算法的情况下,写入2个或更多系统可能会导致数据不一致。如果使用多个本地事务,则无法处理所有错误情况。
避免这种情况的唯一方法是将通信分为多个步骤,并且在每个步骤中仅写入一个外部系统。SAGA模式和变更数据捕获实现(例如Debezium)使用这种方法来确保对多个系统的一致写入操作或将事件发送到Apache Kafka。


 

推荐阅读
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • Android日历提醒软件开源项目分享及使用教程
    本文介绍了一款名为Android日历提醒软件的开源项目,作者分享了该项目的代码和使用教程,并提供了GitHub项目地址。文章详细介绍了该软件的主界面风格、日程信息的分类查看功能,以及添加日程提醒和查看详情的界面。同时,作者还提醒了读者在使用过程中可能遇到的Android6.0权限问题,并提供了解决方法。 ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
  • 什么是大数据lambda架构
    一、什么是Lambda架构Lambda架构由Storm的作者[NathanMarz]提出,根据维基百科的定义,Lambda架构的设计是为了在处理大规模数 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • Sleuth+zipkin链路追踪SpringCloud微服务的解决方案
    在庞大的微服务群中,随着业务扩展,微服务个数增多,系统调用链路复杂化。Sleuth+zipkin是解决SpringCloud微服务定位和追踪的方案。通过TraceId将不同服务调用的日志串联起来,实现请求链路跟踪。通过Feign调用和Request传递TraceId,将整个调用链路的服务日志归组合并,提供定位和追踪的功能。 ... [详细]
  • 项目运行环境配置及可行性分析
    本文介绍了项目运行环境配置的要求,包括Jdk1.8、Tomcat7.0、Mysql、HBuilderX等工具的使用。同时对项目的技术可行性、操作可行性、经济可行性、时间可行性和法律可行性进行了分析。通过对数据库的设计和功能模块的设计,确保系统的完整性和安全性。在系统登录、系统功能模块、管理员功能模块等方面进行了详细的介绍和展示。最后提供了JAVA毕设帮助、指导、源码分享和调试部署的服务。 ... [详细]
  • Tomcat安装与配置教程及常见问题解决方法
    本文介绍了Tomcat的安装与配置教程,包括jdk版本的选择、域名解析、war文件的部署和访问、常见问题的解决方法等。其中涉及到的问题包括403问题、数据库连接问题、1130错误、2003错误、Java Runtime版本不兼容问题以及502错误等。最后还提到了项目的前后端连接代码的配置。通过本文的指导,读者可以顺利完成Tomcat的安装与配置,并解决常见的问题。 ... [详细]
  • Windows系统 查询已开通的端口号和对外开放端口号
    查询端口号开放情况:查看该端口被那个PID所占用;方法一:有针对性的查看端口,使用命令:netstat–ano|findstr“”netstat-a对外开放端口号参考ht ... [详细]
  • springboot基于redis配置session共享项目环境配置pom.xml引入依赖application.properties配置Cookie序列化(高版本不需要)测试启 ... [详细]
author-avatar
mobiledu2502909383
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有