DDIA 读书分享 第五章:Replication,复制滞后问题

简介: DDIA 读书分享 第五章:Replication,复制滞后问题

DDIA 读书分享会,会逐章进行分享,结合我在工业界分布式存储和数据库的一些经验,补充一些细节。每两周左右分享一次,欢迎加入,网站在这里[1]。我们有个对应的分布式&数据库讨论群,每次分享前会在群里通知。如想加入,可以加我的微信号:qtmuniao,简单自我介绍下,并注明:分布式系统群。

复制滞后问题

如前所述,使用多副本的好处有:

  1. 可用性:容忍部分节点故障
  2. 可伸缩性:增加读副本处理更多读请求
  3. 低延迟:让用户选择一个就近的副本访问

引出

对于读多写少的场景,想象中,可以通过使劲增加读副本来均摊流量。但有个隐含的条件是,多副本建的同步得做成异步的,否则,读副本一多,某些副本就很容易出故障,进而阻塞写入。

但若是异步复制,就会引入不一致问题:某些副本进度落后于主副本。

如果此时不再有写入,经过一段时间后,多副本最终会达到一致:最终一致性

在实际中,网络通常比较快,副本滞后(replication lag)不太久,也即这个最终通常不会太久,比如 ms 级别,最多 s 级别。但是,对于分布式系统,谁都不敢打包票,由于网络分区、机器高负载等等软硬件问题,在极端情况下,这个最终可能会非常久。

总之,最终是一个非常不精确的限定词。

对于这种最终一致的系统,在工程中,要考虑到由于副本滞后所带来的一致性问题。

读你所写

image.png

                               read after write

上图问题在于,在一个异步复制的分布式数据库里,同一个客户端,写入主副本后返回;稍后再去读一个落后的从副本,就会发现:读不到自己刚写的内容!

为了避免这种反直觉的事情发生,我们引入一种新的一致性:读写一致性(read-after-write consistency),或者 读你所写一致性(read-your-writes consistency)

若数据库提供这种一致性保证,对于单个客户端来说,就一定能够读到其所写变动。也即,这种一致性是从单个客户端角度来看的一种因果一致性。

那么如何提供这种保证,或者说,实现这种一致性呢?列举几种方案:

  1. 按内容分类。对于客户端可能修改的内容集,只从主副本读取。如社交网络上的个人资料,读自己的资料时,从主副本读取;但读其他人资料时,可以向从副本读。
  2. 按时间分类。如果每个客户端都能访问基本所有数据,则方案一就会退化成所有数据都要从主副本读取,这显然不可接受。此时,可以按时间分情况讨论,近期内有过改动的数据,从主副本读,其他的,向从副本读。那这个区分是否最近的时间阈值(比如一分钟)如何选取呢?可以监控从副本一段时间内的最大延迟这个经验值,来设置。
  3. 利用时间戳。客户端记下本客户端上次改动时的时间戳,在读从副本时,利用此时间戳来看某个从副本是否已经同步了改时间戳之前内容。可以在所有副本中找到一个已同步了的;或者阻塞等待某个副本同步到该时间戳后再读取。时间戳可以是逻辑时间戳,也可以是物理时间戳(此时多机时钟同步非常重要)。
  4. 会有一些实际的复杂 case:
  1. 数据分布在多个物理中心。所有需要发送给主副本的请求都要首先路由到主副本所在的数据中心。
  2. 一个逻辑用户有多个物理客户端。比如一个用户通过电脑、手机多终端同时访问,此时就不能用设备 id,而需要使用用户 id,来保证用户角度的读写一致性。但不同设备有不同物理时间戳,不同设备访问时可能会路由到不同数据中心。

单调读

异步复制可能带来的另外一个问题:对于一个客户端来说,系统可能会发生时光倒流(moving backward in time)

image.png

                           monotonic reads

于是,我们再引入一种一致性保证:单调读(Monotonic reads)

  • 读写一致性和单调读有什么区别?
    写后读保证的是写后读顺序,单调读保证的是多次读之间的顺序。

如何实现单调读?

  1. 只从一个副本读数据。
  2. 前面提到的时间戳机制。

一致前缀读

image.png

                              lower partition

异步复制所带来的第三个问题:有时候会违反因果关系。

本质在于:如果数据库由多个分区(Partition)组成,而分区间的事件顺序无法保证。此时,如果有因果关系的两个事件落在了不同分区,则有可能会出现果在前,因在后

为了防止这种问题,我们又引入了一种一致性:一致前缀读(consistent prefix reads)。奇怪的名字。

实现这种一致性保证的方法:

  1. 不分区。
  2. 让所有有因果关系的事件路由到一个分区。

但如何追踪因果关系是个难题。

副本滞后的终极解决方案

事务!

多副本异步复制所带来的一致性问题,都可以通过事务(transaction)来解决。单机事务已经存在了很长时间,但在数据库走向分布式时代,一开始很多 NoSQL 系统抛弃了事务。

  • 这是为什么?
  1. 更容易的实现。
  2. 更好的性能。
  3. 更好的可用性。

于是复杂度被转移到了应用层。

这是数据库系统刚大规模步入分布式(多副本、多分区)时代的一种妥协,在经验积累的够多之后,事务必然会被引回。

于是近年来越来越多的分布式数据库开始支持事务,是为分布式事务


我是青藤木鸟,一个喜欢摄影的分布式系统程序员。欢迎关注我的公众号:木鸟杂记。如果觉得不错,就点个在看分享一下吧。如果想找人交流分布式系统和数据库,欢迎来论坛:https://distsys.cn/ 提问,点击下方阅读原文可直达。

参考资料

[1]DDIA 读书分享会: https://docs.qq.com/sheet/DWHFzdk5lUWx4UWJq

相关文章
|
运维 监控 Devops
理解并应用DevOps最佳实践的技术指南
【5月更文挑战第22天】本文介绍了DevOps在提升开发效率和保证软件质量中的关键作用,强调文化转变、自动化、持续集成/部署及监控的重要性。文章提出六个最佳实践:建立共同目标、采用敏捷方法、实现自动化、实施CI/CD、加强沟通协作和持续学习改进。Netflix的案例展示了DevOps的成功应用。随着技术发展,DevOps将在软件开发中持续创新。
|
Java Spring
基于Java多线程处理数据
【4月更文挑战第9天】 基于Java多线程处理数据
|
SQL Oracle 关系型数据库
MySQL必知必会:MySQL中的Schema与DataBase
MySQL必知必会:MySQL中的Schema与DataBase
|
SQL 缓存 关系型数据库
SqlAlchemy 2.0 中文文档(三十七)(2)
SqlAlchemy 2.0 中文文档(三十七)
118 2
|
安全 API 数据安全/隐私保护
HarmonyOS学习路之开发篇—安全管理(权限开发)
系统利用内核保护机制来识别和隔离应用资源,可将不同的应用隔离开,保护应用自身和系统免受恶意应用的攻击。默认情况下,应用间不能彼此交互,而且对系统的访问会受到限制。例如,如果应用A(一个单独的应用)尝试在没有权限的情况下读取应用B的数据或者调用系统的能力拨打电话,操作系统会阻止此类行为,因为应用 A 没有被授予相应的权限。
异步时父子线程间的ThreadLocal传递方案
异步时父子线程间的ThreadLocal传递方案
369 0
|
存储 Cloud Native Go
100天精通Golang(基础入门篇)——第18天:深入解析Go语言中的结构体
100天精通Golang(基础入门篇)——第18天:深入解析Go语言中的结构体
98 0
|
消息中间件 tengine 运维
线上问题如何排查处理?这篇全搞定 | 开发者社区精选文章合集(十二)
有哪些常见的线上故障?如何快速定位问题?有哪些排查工具?本合集给你分享线上问题排查技巧!
线上问题如何排查处理?这篇全搞定 | 开发者社区精选文章合集(十二)
java202302java学习笔记第十五天-集合的基本使用3
java202302java学习笔记第十五天-集合的基本使用3
118 0
java202302java学习笔记第十五天-集合的基本使用3
|
存储 数据采集 缓存
布隆过滤器,一文总结快速掌握,你能够get多少?
假如有一个15亿用户的系统,每天有几亿用户访问系统,要如何快速判断是否为系统中的用户呢? 方法一,将15亿用户存储在数据库中,每次用户访问系统,都到数据库进行查询判断,准确性高,但是查询速度会比较慢。 方法二,将15亿用户缓存在Redis内存中,每次用户访问系统,都到Redis中进行查询判断,准确性高,查询速度也快,但是占用内存极大。即使只存储用户ID,一个用户ID一个字符,则15亿*8字节=12GB,对于一些内存空间有限的服务器来说相对浪费。