【转载】细聊冗余表数据一致性(架构师之路)

【转载】细聊冗余表数据一致性(架构师之路)

本文主要讨论四个问题:

(1)为什么会有冗余表的需求

(2)如何实现冗余表

(3)正反冗余表谁先执行

(4)冗余表如何保证数据的一致性

 

一、需求缘起

互联网很多业务场景的数据量很大,此时数据库架构要进行水平切分,水平切分会有一个patition key,通过patition key的查询能够直接定位到库,但是非patition key上的查询可能就需要扫描多个库了。

例如订单表,业务上对用户和商家都有订单查询需求:

Order(oid, info_detail)

T(buyer_id, seller_id, oid)

如果用buyer_id来分库,seller_id的查询就需要扫描多库。

如果用seller_id来分库,buyer_id的查询就需要扫描多库。

这类需求,为了做到高吞吐量低延时的查询,往往使用“数据冗余”的方式来实现,就是文章标题里说的“冗余表”

T1(buyer_id, seller_id, oid)

T2(seller_id, buyer_id, oid)

同一个数据,冗余两份,一份以buyer_id来分库,满足买家的查询需求;

一份以seller_id来分库,满足卖家的查询需求。

 

二、冗余表的实现方案

【方法一:服务同步写】

24a6e9839da29bcb35d0baa58e9c8a59
顾名思义,由服务层同步写冗余数据,如上图1-4流程:

(1)业务方调用服务,新增数据

(2)服务先插入T1数据

(3)服务再插入T2数据

(4)服务返回业务方新增数据成功

优点

(1)不复杂,服务层由单次写,变两次写

(2)数据一致性相对较高(因为双写成功才返回)

缺点

(1)请求的处理时间增加(要插入次,时间加倍)

(2)数据仍可能不一致,例如第二步写入T1完成后服务重启,则数据不会写入T2

如果系统对处理时间比较敏感,引出常用的第二种方案

【方法二:服务异步写】

1c37ee5000920093e7d59dadd48caf86
数据的双写并不再由服务来完成,服务层异步发出一个消息,通过消息总线发送给一个专门的数据复制服务来写入冗余数据,如上图1-6流程:

(1)业务方调用服务,新增数据

(2)服务先插入T1数据

(3)服务向消息总线发送一个异步消息(发出即可,不用等返回,通常很快就能完成)

(4)服务返回业务方新增数据成功

(5)消息总线将消息投递给数据同步中心

(6)数据同步中心插入T2数据

优点

(1)请求处理时间短(只插入1次)

缺点

(1)系统的复杂性增加了,多引入了一个组件(消息总线)和一个服务(专用的数据复制服务)

(2)因为返回业务线数据插入成功时,数据还不一定插入到T2中,因此数据有一个不一致时间窗口(这个窗口很短,最终是一致的)

(3)在消息总线丢失消息时,冗余表数据会不一致

如果想解除“数据冗余”对系统的耦合,引出常用的第三种方案

【方法三:线下异步写】

e79918ae2ee508a6a7ee378c37b70180
数据的双写不再由服务层来完成,而是由线下的一个服务或者任务来完成,如上图1-6流程:

(1)业务方调用服务,新增数据

(2)服务先插入T1数据

(3)服务返回业务方新增数据成功

(4)数据会被写入到数据库的log中

(5)线下服务或者任务读取数据库的log

(6)线下服务或者任务插入T2数据

优点

(1)数据双写与业务完全解耦

(2)请求处理时间短(只插入1次)

缺点

(1)返回业务线数据插入成功时,数据还不一定插入到T2中,因此数据有一个不一致时间窗口(这个窗口很短,最终是一致的)

(2)数据的一致性依赖于线下服务或者任务的可靠性

上述三种方案各有优缺点,但不管哪种方案,都会面临“究竟先写T1还是先写T2”的问题?这该怎么办呢?

 

三、究竟先写正表还是反表

对于一个不能保证事务性的操作,一定涉及“哪个任务先做,哪个任务后做”的问题,解决这个问题的方向是:

【如果出现不一致】,谁先做对业务的影响较小,就谁先执行。

以上文的订单生成业务为例,buyer和seller冗余表都需要插入数据:

T1(buyer_id, seller_id, oid)

T2(seller_id, buyer_id, oid)

用户下单时,如果“先插入buyer表T1,再插入seller冗余表T2”,当第一步成功、第二步失败时,出现的业务影响是“买家能看到自己的订单,卖家看不到推送的订单”

相反,如果“先插入seller表T2,再插入buyer冗余表T1”,当第一步成功、第二步失败时,出现的业务影响是“卖家能看到推送的订单,卖家看不到自己的订单”

由于这个生成订单的动作是买家发起的,买家如果看不到订单,会觉得非常奇怪,并且无法支付以推动订单状态的流转,此时即使卖家看到有人下单也是没有意义的。

因此,在此例中,应该先插入buyer表T1,再插入seller表T2。

however,记住结论:如果出现不一致】,谁先做对业务的影响较小,就谁先执行。

四、如何保证数据的一致性

从二节和第三节的讨论可以看到,不管哪种方案,因为两步操作不能保证原子性,总有出现数据不一致的可能,那如何解决呢?

【方法一:线下扫面正反冗余表全部数据】

91417dd4bc3463ee6dd1d791cb9d38bf
如上图所示,线下启动一个离线的扫描工具,不停的比对正表T1和反表T2,如果发现数据不一致,就进行补偿修复。

优点

(1)比较简单,开发代价小

(2)线上服务无需修改,修复工具与线上服务解耦

缺点

(1)扫描效率低,会扫描大量的“已经能够保证一致”的数据

(2)由于扫描的数据量大,扫描一轮的时间比较长,即数据如果不一致,不一致的时间窗口比较长

有没有只扫描“可能存在不一致可能性”的数据,而不是每次扫描全部数据,以提高效率的优化方法呢?

【方法二:线下扫描增量数据】

9b8a4e73fd2426d511f9c266f803c25d
每次只扫描增量的日志数据,就能够极大提高效率,缩短数据不一致的时间窗口,如上图1-4流程所示:

(1)写入正表T1

(2)第一步成功后,写入日志log1

(3)写入反表T2

(4)第二步成功后,写入日志log2

当然,我们还是需要一个离线的扫描工具,不停的比对日志log1和日志log2,如果发现数据不一致,就进行补偿修复

优点

(1)虽比方法一复杂,但仍然是比较简单的

(2)数据扫描效率高,只扫描增量数据

缺点

(1)线上服务略有修改(代价不高,多写了2条日志)

(2)虽然比方法一更实时,但时效性还是不高,不一致窗口取决于扫描的周期

有没有实时检测一致性并进行修复的方法呢?

【方法三:实时线上“消息对”检测】

14dafce4ce22db72057d011e26dfdbc2
这次不是写日志了,而是向消息总线发送消息,如上图1-4流程所示:

(1)写入正表T1

(2)第一步成功后,发送消息msg1

(3)写入反表T2

(4)第二步成功后,发送消息msg2

这次不是需要一个周期扫描的离线工具了,而是一个实时订阅消息的服务不停的收消息。

假设正常情况下,msg1和msg2的接收时间应该在3s以内,如果检测服务在收到msg1后没有收到msg2,就尝试检测数据的一致性,不一致时进行补偿修复

优点

(1)效率高

(2)实时性高

缺点

(1)方案比较复杂,上线引入了消息总线这个组件

(2)线下多了一个订阅总线的检测服务

however,技术方案本身就是一个投入产出比的折衷,可以根据业务对一致性的需求程度决定使用哪一种方法。我这边有过好友数据正反表的业务,使用的就是方法二。

==【完】==

 

【转自】58沈剑 架构师之路

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/119360.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 三天实现独立分析宏基因组数据(有参、无参和分箱等)[通俗易懂]

    三天实现独立分析宏基因组数据(有参、无参和分箱等)[通俗易懂]在广大粉丝的期待下,《生信宝典》联合《宏基因组》在2019年11月1-3日,北京鼓楼推出《宏基因组分析》专题培训第六期,为大家提供一条走进生信大门的捷径、为同行提供一个宏基因组分析学习和…

    2022年6月3日
    70
  • 《Android平台开发之旅》学习笔记

    《Android平台开发之旅》学习笔记第三章:Android应用程序组件3.2Android应用程序组件Activity活动–形象大使Service服务–老黄牛BroadcastReceiver广播接收器–倾听者ContentProvider内容提供者3.3组件应用机制3.1.1组件之间的交互机制Intent(意向)组件实现组件之间的交互,马上要执行的动作3..1.2未决意意向Pending…

    2022年9月28日
    4
  • Navicat Premium MAC 破解版 激活码-激活码分享

    (Navicat Premium MAC 破解版 激活码)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.htmlMLZPB5EL5Q-eyJsa…

    2022年3月21日
    119
  • Splint介绍

    Splint介绍1 静态分析工具 VS 编译器编译器负责把 C 源程序快速 高效地转变为可执行文件 不对代码做类型检查 特别是对分别编译的程序 有益于做到快速与高效 Lint 没有 高效 的要求 可以花更多时间对代码进行更深入 仔细的检查 2 C 代码静态分析工具 Its4 读取一个或多个 C C 源程序 将每个源程序分割成函数标志流

    2025年11月11日
    2
  • saga分布式事务_spring分布式事务实现

    saga分布式事务_spring分布式事务实现Saga分布式事务解决方案与实践我先介绍一下我自己,我叫姜宁,来自于华为开源研究中心,现在负责的是ServiceComb这个开源项目。ServiceComb这个项目已经进到Apache孵化,应该是去年11月份时进到Apache孵化的,这个月我们帮刚发了1.0M1版,但对于SAGA来说我们属于探索的阶段,发布了0.1.0。我参与过Apache一些项目,我也是Apache的Member,…

    2022年9月19日
    6
  • toast弹窗什么意思_app加弹窗软件

    toast弹窗什么意思_app加弹窗软件//界面toast提示/*使用方法Toast(‘这是一个弹框’,2000)*/functionToast(msg,duration){ duration=isNaN(duration)?3000:duration; varm=document.createElement(‘div’); m.innerHTML=msg; m.style.cssText=…

    2022年9月24日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号