本文是《设计数据密集型应用》第5章学习笔记。
什么是Replication
Replication是在多台机器上维护的相同的数据,即副本。保存副本的原因有以下几种:
-
减小延迟:使得地理位置上数据离访问者更近。
-
高可用:部分机器宕机后仍可继续运行。
-
scale out-容量伸缩。
Replication带来的问题以及应对方案
如果我们的数据不会发生变化,那么就容易了,只需要把数据拷贝到各个副本就OK了。但是数据是不断变化的,我们需要更新不同的节点的数据。Replication带来的难点主要围绕这一点。主流的应对算法有3种,几乎所有分布式数据库采用的算法都来自其中:
-
single-leader Replication-单主
-
multi-leader Replication-多主
-
leadless Replication
主从架构
更新到数据库的数据更新操作,需要同步到各个节点,否则节点的副本数据就不一致了,最常见的方案是主备架构(master-slave)。其工作机制如下图:
图中有一主二从,写操作都会请求到主节点,主节点更新会将数据更新内容也同步到从节点。读客户端既可以读主也可以读从。 这种架构在很多数据库(MySQL、PostgreSQL)以及消息中间件(Kafka、RabbitMQ)中都有采用。 副本同步/异步更新 主从架构一个比较重要的问题就是副本更新的机制,根据需求可以配置采用同步和异步2种方式。 先看一个实际的例子。
从1同步更新,从2异步更新。客户端在写主的时候,必须同步等待从1也更新完毕才返回。而从2是异步更新,有可能存在延迟。 同步更新的优点是可以确保数据被最新更新到从上,但是带来比较大的问题是增加了写的耗时,而且从可能会异常,从而直接阻塞了写操作。因此通常我们不会设置多个主均同步,一般只设置其中一个从同步,而其他从异步。这样可以确保最新的数据存在于2个节点上。这种机制也叫半同步。 如果设置所有从均异步更新,那么叫全异步。全异步的一个问题是更新操作不保证持久化成功,因为仅更新到主,如果主崩溃,那么数据就丢失了。但是在很多情况下也得到了应用。 新增从节点 什么时候需要新增从节点?
-
增加从节点数量
-
有从节点挂掉,需要进行替换
增加从节点我们需要拷贝主节点的全量数据。但是主节点的写操作一直持续,我们又不可能让主停写,那么拷贝操作如何进行呢?通常分为如下3步
-
保存主在某时刻的快照信息;
-
将快照拷贝到从节点;
-
从开始同步从快照开始的新的写操作;
-
当从处理完毕从快照开始的所有写操作,就算跟上了主的进度;
节点下线 在主从架构中,由于各种原因(比如机器挂掉,重启以更新软件等)会有节点下线,但是我们必须保证整体系统是高可用的。 从节点下线 从节点会在本地磁盘记录从主节点接收的数据变更日志,当崩溃会重启后重新恢复时,会从上一次成功处理的进度开始继续处理,直至追上进度,重新从主节点接收数据变更信息。 主节点下线-failover 主节点的失败恢复稍微复杂一些,也叫做failover:
-
其中一个从节点升级成为主节点
-
客户端需要感知这个变更,写操作需要发送到新的主
-
其他从节点需要从新的主接收数据变更消息
failover可以手动进行,也可以自动进行,自动进行一般是如下步骤:
-
主节点异常检测。一般采用心跳超时检测。
-
挑选新的主。一般采用某种选举算法。
-
更新配置,让客户端和从节点感知到新的主。
副本同步延迟可能导致的问题
写后读一致性(read-after-write consistency)
如图所示,在主从架构下,一个客户端如果写后立即读,如果读的不是主,那么可能读不到自己最新写入的数据。如果要能立即读到写入的数据,那么则需要保证read-after-write consistency。如何保证呢?
-
当读到可能被修改的信息的时候,读主,比如社交网站用户的个人信息;
-
如果大量信息都从主读的话,那会带来问题。由此一个优化的方法可以是,记录信息上一次的更新时间戳,根据时间来确定是读主还是从。
单调读一致性(Monotonic Consistency) 单调读一致性是指,读到的永远比之前读到的数据新。 让我们看一个实际的例子:User2345第一次读到了User1234插入的数据,但是第二次确没读到。
那么如何保证单调读一致性呢?通常做法是让每个读客户端总是读同样的节点。 前缀一致性读(Consistent Prefix Read) 即写的顺序是什么样,读到的顺序就是什么样。从下图可以看出这种问题是如何产生的。
常见解决办法是让有关联关系的写操作都落在同一个副本节点。