背景

简而言之,Replication是我们在部署分布式或集群中的一种模式,Replication可以了解为复制或冗余。

使用Replication的原因也很明显:

  1. 可以在一定程度上,降低不同地区用户访问的延迟
  2. 提供部分宕机情况下的可用服务
  3. 提高服务的最高吞吐量

众所周知,Replication分有三种,Single-Leader、Multi-Leader以及Leader-Less。其中对于我来说,最常见的则是Single-Leader,即主从模式。

Single-Leader和Multi-Leader都是属于Leader-based,这类型的规则也很简单:

  1. 用户的写入请求必须通过Leader,Leader接收到自身写入后再同步给Followers
  2. 用户的读取请求则可以从所有节点中读取,不过一般只从Follower读。

Leader-based Replication分同步和异步,甚至还有位于中间的半同步。

同步意味着用户请求了写入Leader后,必须要等到Leader的数据同步到全部Follower后才将此次写入请求结果返回给用户。也就是说,同步需要等很长时间。

而异步则是用户请求了Leader后,Leader马上就返回了一个“假结果”,然后Leader再自己偷偷地将数据同步给各个Follower。这样的处理很快,但是在用户得到结果后却不能保证服务后续操作的可靠性。

当然还有中间的半同步模式,则是Leader接收到用户的请求后也先将数据同步给各个Follower,若是其中有那么一个Follower完成了这个同步操作,Leader就立刻返回结果给用户了。这种处理方法比同步快,比异步靠谱。

同步异步就到此吧。

为了享受Replication的优点,我们也必须要忍受它各种各样的缺点,如新增Follower的方法、部分节点宕机恢复后的数据恢复、脑裂情况下的处理方式、超时时间的调优。

幸运的是,问题先来,解决方法总会是陆续而来。当然以上的问题都不是本笔记的重点,本笔记的重点是谈谈数据的一致性。

问题

读取自己的写入数据(Reading your own writes)

某场景下,或者说某种接口中,用户提交了新增的数据或者更新了数据后,需要预览到自己刚才提交的数据。

如果在同步模式下,那么这个场景根本没有这个问题。然而这种做法简直是在牺牲用户体验来做较为完美的一致性,我不太认同这种trade-off。

我们还是谈谈异步模式下这个场景的问题。异步模式下,由于写入数据只到达了Leader,而Follower可能还没有同步。而如果这个时候用户读取了没有同步到的Follower,就会有一种疑惑:我刚才是否执行失败了。

而这里要做的保证就是,起码用户读取自己刚刚写入的数据是没有问题的。

这里需要引入写后读一致性(read-after-write consistency),是一种较弱的一致性。要实现这种一致性一般需要:

  1. 当用户读取某一样可以会被修改的数据时,请从Leader读
  2. 若大多数数据都可能会被修改,还遵循着1则会无限方法Leader的负担,这种情况下,我们可以先检测需要读取的数据是否最近被修改过(比如说一分钟内),再判断从哪里读
  3. 当然客户端也可以自个保留这一个逻辑时间戳,在请求的时候就带上这个时间戳,好让服务端评断是否最近执行过更新操作

如此看来还是比较简单的。

如果场景迁移到多个Leader的多个数据仓库下的话,可能会复杂一点。再有就是同一个用户账号,在电脑端上更新了,然后短时间内在移动设备上读取,如此的跨设备写后读一致性也是需要考虑在内的。

单调读(Monotonic Reads)

如果一个用户每次读取的Replica节点不一样,就有可能会产生一种现象,倒退(回退)。

比如说,一个叫煎鱼的用户读取了2号节点,看到了2018-05-06 00:05:19的新闻,然后下次刷新读取了3好节点,由于3号节点具有较大的网络延迟,新闻只同步到2018-05-06 00:05:00,就会给用户一个假象,05:00到05:19的新闻怎么不见了。

当然要保证单读也很简单,就是同一个用户只读取同一个机器节点。

常见的现实方式就是负载均衡,一般以地区划分用户的访问节点,或者使用hash。

一致前缀读/一致顺序读(Consistent Prefix Reads)

存在一种情况,一个叫煎鱼的用户在不同的节点中读取了多条数据,由于时间延迟不一样,2号机器比3号机器慢,因此2号00:05:19的新闻显示在3号机器的00:05:40的新闻的后面,导致新闻的时间很不连贯。

当然,如果新闻的时间不连贯是没什么问题的,最严重的此类问题是发生在聊天场景中。如果聊天中各个节点的消息没有按照时间排序,就会给用户造成极大的消极影响。

因此,引入一致前缀读(个人觉得按照意思翻译成一致顺序读好了解点),意思就是在写入的时候是什么样的一个顺序,在读取的时候也要按照这个样的顺序来读取。

如,既然按照时间顺序来写入,就要按照时间顺序来解读。如果按照别的序号顺序写入,就要按照那个序号顺序来读取。

关键点就在于读取的客户端需要处理一下。

问题是否必要

以上数据一致性问题,都是基于异步模式下的假设。这些一致性的保证并不是说非做不可,但是不做则将会对用户体验造成重大影响。

如果不关心用户体验的话,那么也没有必要采取异步模式了是吧。

先这样吧

若有错误之处请指出,更多地关注煎鱼

Categories: Distributed

发表评论

电子邮件地址不会被公开。 必填项已用*标注