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

1,245 阅读6分钟

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

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

  1. 可用性:容忍部分节点故障

  2. 可伸缩性:增加读副本处理更多读请求

  3. 低延迟:让用户选择一个就近的副本访问

引出

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

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

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

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

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

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

读你所写

read after write

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

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

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

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

  1. 按内容分类。对于客户端可能修改的内容集,只从主副本读取。如社交网络上的个人资料,读自己的资料时,从主副本读取;但读其他人资料时,可以向从副本读。

  2. 按时间分类。如果每个客户端都能访问基本所有数据,则方案一就会退化成所有数据都要从主副本读取,这显然不可接受。此时,可以按时间分情况讨论,近期内有过改动的数据,从主副本读,其他的,向从副本读。那这个区分是否最近的时间阈值(比如一分钟)如何选取呢?可以监控从副本一段时间内的最大延迟这个经验值,来设置。

  3. 利用时间戳。客户端记下本客户端上次改动时的时间戳,在读从副本时,利用此时间戳来看某个从副本是否已经同步了改时间戳之前内容。可以在所有副本中找到一个已同步了的;或者阻塞等待某个副本同步到该时间戳后再读取。时间戳可以是逻辑时间戳,也可以是物理时间戳(此时多机时钟同步非常重要)。

会有一些实际的复杂 case:

  1. 数据分布在多个物理中心。所有需要发送给主副本的请求都要首先路由到主副本所在的数据中心。

  2. 一个逻辑用户有多个物理客户端。比如一个用户通过电脑、手机多终端同时访问,此时就不能用设备 id,而需要使用用户 id,来保证用户角度的读写一致性。但不同设备有不同物理时间戳,不同设备访问时可能会路由到不同数据中心。

单调读

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

monotonic reads

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

  • 读写一致性和单调读有什么区别?

    写后读保证的是写后读顺序,单调读保证的是多次读之间的顺序。

如何实现单调读?

  1. 只从一个副本读数据。

  2. 前面提到的时间戳机制。

一致前缀读

lower partition

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

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

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

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

  1. 不分区。

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

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

副本滞后的终极解决方案

事务!

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

  • 这是为什么?
  1. 更容易的实现。

  2. 更好的性能。

  3. 更好的可用性。

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

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

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


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

题图故事

呼伦贝尔黑山头,自己跑回家的马

呼伦贝尔黑山头,自己跑回家的马

用通俗的语言解释下:Spark 中的 RDD 是什么

DDIA 读书分享 第五章:Replication,主从

数据库存储层都涉及到哪些工作?

计算机极简公开课推荐