详解mysql和消息队列数据一致性问题

目录

前言

保持系统数据同步(双写问题)

消息队列消息丢失的问题

总结


前言

在当今互联网飞速发展的时代,随着业务复杂性的不断增加,消息队列作为一种重要的技术手段,越来越多地被应用于各种场景。它们不仅能有效解耦系统各部分之间的联系,还能够平衡流量峰值,提高系统的整体性能。

在本文中,我将重点分析一家医美公司的团队在使用消息队列时的奇葩做法。这家公司的技术团队选择了 Kafka 作为主要的消息队列,但在某些情况下却出现了数据未能成功消费的问题。为了应对这个挑战,他们又引入了 RocketMQ,并通过其延迟机制来弥补之前的不足。然而,这样的做法是否合理?这背后又隐藏着哪些技术思考与团队协作的缺陷?

接下来,我们将深入探讨这家公司的消息队列应用,以及其中的潜在问题与改进方案。

下面是我画的他们业务的一个架构图:

                                                                        <图一>

在这个架构图中,数据首先从MySQL数据库流入“业务1”,并通过Kafka进行消息传递。然后,数据被传递到“业务2”进行处理。处理完成后,系统会判断是否成功。如果成功,则数据继续存储到MySQL中;如果未成功处理,则会将消息发送到RocketMQ进行重新处理。

这个架构是非常简单,加入了 RocketMQ 就一个诉求就是消息不丢失,真的能做到吗?其实有几个问题,第一个问题系统同步也就是双重写入问题 第二个问题就是加入RocketMQ 和不合理,下面我们逐一讲解。

保持系统数据同步(双写问题)

我们业务系统有两个双写:业务1的1和2。业务2的5和6。图中这样的设计看起来很简单但是有一些严重的问题。

先用一个流程图来说明双写严重的问题:

                                                                <图二>

在这个例子中,两个客户同时想要更新项X:

客户端l 想要将值设置为A,客户端2 想要设置为B 。两个客户端首先将新值写入数据库,然后将其写入消息队列中。由于实机不凑巧,这些请求交叉了:数据库首先看到来自客户端1 的写入,将值设置为A,然后看到来自客户端2 的写入,将值设置为B ,因此数据库中的最终值为B 。而消息队列首先看到来自客户端2 的写人,然后才是客户端1 的写人,所以消息队列中的最终值是A。这两个系统将永远不一致,即使目前还没有发生错误。

除非有一些额外的并发监测机制,或者加入版本向量或者加入分布式锁。否则甚至不会注意到并发写入,一个值悄悄覆盖另一个值。

我们可以总结到双重写入的一个问题就是值会可能被覆盖。

还有一个很难发现的问题就是客户端1或者客户端2写入数据库成功了但是写入消息队列失败了,这不是一个并发问题,这是一个容错问题,也会造成两个系统相互不一致的结果,这是致命的。确保数据库和消息队列都成功或者失败是个原子提交问题,需要用到2pc 等分布式事务相关的技术。

这两个问题都是致命的,必须解决,数据库的主从复制给我了启发,不管如何并发,从库总是订阅主库的binglog,保证了数据的一致性。我们发现我们这个例子其实可以看成两个主库的双写问题,有没有一种技术把两个主库的双写问题转成主从机制呢?那么问题不就解决了吗?还真的是有这种方案,就是CDC 技术。于是我的架构就发生了一种变化。

                                                                <图三>

CDC 技术是对系统同步的里程碑,他借助了主从的思想,软件开发工程师开始认识到日志的重要性,再也不认为,数据库复制日志的问题被认为是数据库内部实现细节,他们开始尝试解析日志,捕获数据的变化,写入时立即将更改作为一种流来发布,那么业务只需要消费这些数据就可以了。现在很多数据库都提供了CDC解决方法。mysql 在国内用的最多的是canal。前面讲过就是canal作为一个从库似的获取主库的数据,并且她集成了快照技术也就是说会记录位置的偏移量,以便在快照处理完成后,知道哪一点开始应用修改。canal 具体问题,可以参考相关的文档。

消息队列消息丢失的问题

图一我们发现了该团队用了RocketMQ和kafka 就是解决业务2消息丢失的问题,RocketMQ在哪种情况下会用到,我们将详细分析下:

只有一种情况下,就是消费者出现error 或者异常,代码能捕获到这些问题,调用RocketMQ,用它的延迟队列。其实又有几中假设:

  1. 如果消费者出现了代码本身的bug ,应该在测试阶段就应该发现的,或者经过压测,这是代码问题,应该避免的

  2. 第二种情况就是业务二入库失败了,会把消息投递到RocketMQ,用它的延迟队列过段时间数据库可能就好了,就一定能捕获吗,成功写入到RocketMQ吗?不一定会。

  3. 如果消费者在处理一半业务就突然挂掉了还能写入到RocketMQ吗,不一定会,消息还是丢失了。

经过分析发现只有第二种情况可能,为了一种可能的情况,引入RocketMQ当然是不合适的,是弊端大于利,而且系统更为复杂,不仅要监控kafka 而且也要监控RocketMQ。

那么我们就想kafka 能不能让消息不丢失呢。当然是可以的呀

在消息队列的处理中,常见的情况有以下几种:

零次处理(At-most-once):消息可能丢失,但不会被重复处理。

一次处理(At-least-once):消息一定会被处理,但可能会被处理多次。

精确一次(Exactly-once):确保每条消息只被处理一次。

消息不丢失我们可以用 At-least-once 消息设计模式

kafka 应该从生产者,消费者,还有服务端 去讨论消息不丢失:

生产者来说:

  1. 使用带回调方法的API。我们就知道消息有没有成功

  2. 设置参数 acks=1 (-1,0,1)。0生产者在发送消息后不等待任何确认,1 领导者(leader)在成功写入消息后发送确认。 -1 所有的副本(包括领导者和跟随者)都必须确认消息已被成功写入。

  3. 设置参数 retries=3。重试次数,如果次数到了还是失败,我们可以写入日志中

  4. 设置参数 retry.backoff.ms=300。每次重试间隔时间

服务端:

replication.factor>1。 (分区副本的个数),至少一个主分区,多个副本节点

Min.insync.replicas>1。 (isr 最少的副本数量)。

Unclean.leader.election.enable = false。 (是否能把非ISR集合中的副本选举为leader 副本)

定义:ISR 是指与当前主分区(Leader)保持同步的所有副本(Replica)。这些副本在数据写入主分区时,能够及时接收到并确认写入的数据。这是重要的,如果选择了非ISR 节点作为leader ,那么就会造成数据丢失。

消费者:

消费者是根据offset 来确定消息的消费位置,可以手动提交offset,就是业务结束的时候

Enable.auto.commit = false (手动提交偏移量)

具体的代码可以参考相关的文档。

得出了最终的架构是这样的。

总结

这篇文章我们详细介绍了cdc 技术,kafka 如何消息不丢失,同时也详细阐述了架构的不合理性以及合理性的架构是如何演变的。所以说选择一个好的团队特别重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/55153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目:微服务即时通讯系统客户端(基于C++QT)]四,中间界面搭建和逻辑准备

四&#xff0c;中间界面搭建 前言:当项目越来越复杂的时候&#xff0c;或许画草图是非常好的选择 一&#xff0c;初始化中间窗口initMidWindow void mainWidget::initMidWindow() {//使用网格布局进行管理QGridLayout* layout new QGridLayout();//距离上方 20px 的距离&…

Arthas redefine(加载外部的.class文件,redefine到JVM里 )

文章目录 二、命令列表2.2 class/classloader相关命令2.2.3 redefine&#xff08;加载外部的.class文件&#xff0c;redefine到JVM里 &#xff09;举例1&#xff1a;加载新的代码&#xff0c;jad/mc 命令使用举例2&#xff1a;上传 .class 文件到服务器的技巧 本人其他相关文章…

值传递和引用传递

值传递和引用传递是函数参数传递的两种类型&#xff0c;一般而言&#xff0c;基本数据类型都是值传递&#xff0c;数组和对象采用引用传递减少对象复制开销&#xff0c;但也有特例。 值和引用传递本质一样 值传递是拷贝值到函数参数&#xff0c;引用传递是拷贝引用(或者对象的指…

矿石运输船数据集、散货船数据集、普通货船数据集、集装箱船数据集、渔船数据集以及客船数据集

海船&#xff1a;用于船只检测的大规模精准标注数据集 我们很高兴地介绍一个新的大规模数据集——海船&#xff0c;该数据集专为训练和评估船只目标检测算法而设计。目前&#xff0c;这个数据集包含31,455张图像&#xff0c;并涵盖了六种常见的船只类型&#xff0c;包括矿石运…

Python基础语句教学

Python是一种高级的编程语言&#xff0c;由Guido van Rossum于1991年创建。它以简单易读的语法和强大的功能而闻名&#xff0c;被广泛用于科学计算、Web开发、数据分析等领域。 Python的应用领域广泛&#xff0c;可以用于开发桌面应用程序、Web应用、游戏、数据分析、人工智能等…

计算机视觉实战项目4(图像分类+目标检测+目标跟踪+姿态识别+车道线识别+车牌识别+无人机检测+A*路径规划+单目测距与测速+行人车辆计数等)

往期热门项目回顾&#xff1a; 计算机视觉项目大集合 改进的yolo目标检测-测距测速 路径规划算法 图像去雨去雾目标检测测距项目 交通标志识别项目 yolo系列-重磅yolov9界面-最新的yolo 姿态识别-3d姿态识别 深度学习小白学习路线 AI健身教练-引体向上-俯卧撑计数…

Python的异步编程

什么是协程&#xff1f; 协程不是计算机系统提供&#xff0c;程序员人为创造。 协程也可以被称为微线程&#xff0c;是一种用户态内的上下文切换技术。简而言之&#xff0c;其实就是通过一个线程实现代码块相互切换执行。 实现协程有那么几种方法&#xff1a; greenlet&…

Centos怎么执行脚本

方法一&#xff1a;切换到shell脚本所在的目录&#xff08;此时&#xff0c;称为工作目录&#xff09;执行shell脚本 cd /data/shell ./hello.sh 方法二&#xff1a;以绝对路径的方式去执行bash shell脚本 /data/shell/hello.sh 方法三&#xff1a;直接使用bash 或sh 来执行…

CSS外边距

元素的外边距&#xff08;margin&#xff09;是围绕在元素边框以外&#xff08;不包括边框&#xff09;的空白区域&#xff0c;这片区域不受 background 属性的影响&#xff0c;始终是透明的。 为元素设置外边距 默认情况下如果不设置外边距属性&#xff0c;HTML 元素就是不会…

Linux shell编程学习笔记84:tee命令——显示保存两不误

0 引言 在前面的学习笔记中&#xff0c;我们经常使用echo命令和输出重定向来生成脚本文件或演示文件&#xff0c;其实Linux提供了一个可以从标准输入读取数据&#xff0c;并输出成文件的命令——tee。 1 tee命令 的帮助信息、功能、命令格式、选项和参数说明 1.1 tee命令 的…

软件测试学习笔记丨Pytest 学习指南

本文转自测试人社区&#xff0c;原文链接&#xff1a;https://ceshiren.com/t/topic/32336 基本介绍 pytest框架是一个成熟&#xff0c;全面的测试框架&#xff0c;具有非常丰富的第三方插件&#xff0c;并且可以自定义扩展 比如&#xff1a;pytest-selenium , pytest-html ,…

MySQL高阶2004-职员招聘人数

目录 题目 准备数据 分析数据 实现 题目 一家公司想雇佣新员工。公司的工资预算是 70000 美元。公司的招聘标准是&#xff1a; 雇佣最多的高级员工。在雇佣最多的高级员工后&#xff0c;使用剩余预算雇佣最多的初级员工。 编写一个SQL查询&#xff0c;查找根据上述标准雇…

net core mvc 数据绑定 《1》

其它的绑定 跟net mvc 一样 》》MVC core 、framework 一样 1 模型绑定数组类型 2 模型绑定集合类型 3 模型绑定复杂的集合类型 4 模型绑定源 》》》》 模型绑定 使用输入数据的原生请求集合是可以工作的【request[],Querystring,request.from[]】&#xff0c; 但是从可读…

unity一键注释日志和反注释日志

开发背景&#xff1a;游戏中日志也是很大的开销&#xff0c;虽然有些日志不打印但是毕竟有字符串的开销&#xff0c;甚至有字符串拼接的开销&#xff0c;有些还有装箱和拆箱的开销&#xff0c;比如Debug.Log(1) 这种 因此需要注释掉&#xff0c;当然还需要提供反注释的功能&am…

前端 vue3 对接科大讯飞的语音在线合成API

主要的功能就是将文本转为语音&#xff0c;可以播放。 看了看官方提供的demo&#xff0c;嗯....没看懂。最后还是去网上找的。 网上提供的案例&#xff0c;很多都是有局限性的&#xff0c;我找的那个他只能读取第一段数据&#xff0c;剩下的不读取。 科大讯飞的接口&#xf…

监控告警功能详细介绍及操作演示:运维团队的智能保障

在当今这个信息化高速发展的时代&#xff0c;运维团队面临着前所未有的挑战。为了确保系统的稳定性和高效运维&#xff0c;监控告警功能成为了运维团队不可或缺的得力助手。本文将详细介绍我们的监控告警功能&#xff0c;并结合实际操作页面进行演示&#xff0c;帮助运维团队更…

25中国烟草校园招聘面试问题总结 烟草面试全流程及面试攻略

开头附上工作招聘面试必备问题噢~~包括综合面试题、无领导小组面试题资源文件免费&#xff01;全文干货。 工作招聘无领导小组面试全攻略最常见面试题&#xff08;第一部分&#xff09;共有17章可用于国企私企合资企业工作招聘面试面试必备心得面试总结资源-CSDN文库https://d…

springboot整合seata

一、准备 docker部署seata-server 1.5.2参考&#xff1a;docker安装各个组件的命令 二、springboot集成seata 2.1 引入依赖 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId>&…

【架构】NewSQL

文章目录 NewSQLTiDBTiDB 主要组件特点使用场景安装与部署 推荐阅读 NewSQL NewSQL是一种数据库管理系统(DBMS)的类别&#xff0c;它结合了NoSQL数据库的可扩展性和传统SQL数据库的事务一致性。具体来说&#xff0c;NewSQL数据库旨在解决传统关系型数据库在处理大规模并发事务…

C# C++ 笔记

第一阶段知识总结 lunix系统操作 1、基础命令 &#xff08;1&#xff09;cd cd /[目录名] 打开指定文件目录 cd .. 返回上一级目录 cd - 返回并显示上一次目录 cd ~ 切换到当前用户的家目录 &#xff08;2&#xff09;pwd pwd 查看当前所在目录路径 pwd -L 打印当前物理…