高吞吐量和低延迟的主从式repli

有不同种类的数据复制模型——主要是, 主从式 (Couchbase、MongoDB、Espresso), 主人-主人 (BDR代表PostgreSQL,GoldenGate代表Oracle),以及 无主的 (迪纳摩,卡珊德拉)。 本文仅讨论键值存储中的主从复制。

在主从复制模型中,单个数据分区有一个主分区,还有一个或多个副本,它们本质上是从分区,跟随主分区中的数据。 客户端应用程序将键值发送给主服务器,随后,键值从主服务器发送到副本服务器。

本文从一些概念开始,如排序、单调递增的***、快照、使用仅追加写入的MVCC和压缩。 然后,我们将解释如何使用增量快照或时间点快照完成主从复制,它们之间的权衡,以及何时最好使用其中一个。 最后,我们简要讨论了一个流行的大数据平台Couchbase如何将这些用于数据复制。

对数据进行分区,将分区分配给物理节点,检测和处理节点故障,协调不同的数据分支,等等,对于启动分布式KV存储非常重要。 本文不讨论其中的任何一个——只讨论单个数据分区的主从复制。

复制数据的一种方法是在我们需要的时候获取源的完整拷贝。 虽然实现起来非常简单,但在OLTP数据库或具有实时工作负载的KV存储中,它用处不大,因为复制副本无法实时获得这些数据库同步软件 更新。

对于任何经常需要处理大量数据的复制协议来说,仅发送更改(增量)是一项重要功能。 但这是以额外的复杂性为代价的,因为需要订购和快照,我们将在下面的章节中讨论。

顺序很重要,因为它允许应用程序推理数据的因果关系——它允许应用程序知道一个操作发生在另一个操作之前还是之后。 在KV商店中,订单用于标识商店中给定**的最新值,还表示商店接收**的顺序。 单调递增的***是对商店中的键值进行“排序”的一种方式。 存储中的每个键值对都有一个与之相关联的唯一***,该***随着存储接收新键值而单调增加。

操作

价值

序号

插入

K1

第五颅神经的眼支

SEQ1

插入

K2

V2

SEQ2

更新

K1

“V1”

SEQ3

插入

K3

V3

SEQ4

在上面的例子中,K2-V2在K1-V1之后被商店接收;“K1-V1”在K2-V2之后被商店接收,以此类推。 因此,SEQ4 > SEQ3 > SEQ2 > SEQ1。 如果商店是追加的,借助SEQ3 > SEQ1,我们可以确定K1的(最新)值是V1,而不是V1。

单调递增***的使用主要是在时间点快照中,我们将在后面讨论。

在最基本的意义上,快照(完整的快照)是一个实例上的KV存储的不可变视图。 这也是该实例中KV存储的一致视图。

我们将增量快照定义为KV存储在一段时间内接收的键值对的不可变副本。 我们称之为增量快照,因为快照不包含存储中的所有键值,而只包含在前一个增量快照形成后接收到的键值,直到创建当前快照。 连续的增量快照提供了到特定点的千伏存储的一致视图;也就是说,直到创建最后一个快照。

多版本并发控制(MVCC)是一种在KV存储中使用的并发控制方法,用于支持并发的读取器和写入器。 处理并发的最简单方法是使用读写锁。 但是在处理大量数据的分布式千伏存储中,MVCC被证明比锁更有用。 MVCC有助于实现更高的吞吐量和更低的读写延迟。

MVCC是通过在KV存储上使用快照和仅附加写入实现的。 在下面的示例中,假设当增量快照1上有读取器时,写入器更新了**B。 带有锁定的并发控制要求变异实体等待,直到整个快照的复制完成。 但是,使用仅附加的MVCC方法,即使正在读取当前快照,对**B的写入也可以继续进行。

高吞吐量和低延迟的主从式repli对于更高级的读者来说,当KV存储使用更复杂的数据结构来存储数据时,也可以使用MVCC。 下面的例子展示了如何在一个仅附加的b+树中实现MVCC。 假设当当前增量快照上有读取器时,由写入器更新**B。

高吞吐量和低延迟的主从式repli

使用仅附加的MVCC方法,即使读取当前快照,对键B和B+树中相关分支的写入也可以继续进行,如下所示。

高吞吐量和低延迟的主从式repli

由G和G’的B+树根表示的两个重叠快照代表了在两个时间实例上的KV存储的一致视图。

由于快照是不可变的,所以对**的更新只被附加到KV存储的末尾,因此,存储的内存使用最终将远远超过活动键值所需的内存。 因此,KV存储需要定期合并较旧的快照,并在称为压缩的后台任务中删除重复/陈旧的键值。 压缩减少了KV存储使用的内存。

压缩触发器可以是内存阈值、固定时间间隔或两者的组合。

高吞吐量和低延迟的主从式repli

可以通过发送一系列连续的增量快照来复制千伏存储。 如上所述,在仅追加写入方法中,新的键值和更新仅被追加到存储中。 接收到一批项目后,将创建一个不可变的快照,并准备好将其发送到副本节点。 创建此快照后,存储接收的键值将进一步追加到存储中,并将成为下一个快照的一部分。 然后,复制客户端一个接一个地获取这些不可变的增量快照,并获得与源一致的存储视图。 注意:这不要求每个千伏对都有***,但我们需要确定增量快照的顺序。

高吞吐量和低延迟的主从式repli

这种方法的一个缺点是KV存储的源端可以将几个不可变的增量快照压缩成一个快照。 现在,如果压缩发生在复制客户端收到最后一个压缩的快照之前,则客户端必须从快照的开始处接收完全压缩的快照。 假设有五个不可变的增量快照— snp1、snp2、snp3、snp4,并且客户端已经接收到多达snp3 —然后压缩运行,源端的四个快照都被压缩为一个快照SNP 1’。 现在,客户不能从snp3恢复;它必须回滚之前接收到的快照(直到snp3 ),并且必须完全接收SNP 1’。

高吞吐量和低延迟的主从式repli

我们可以通过在每个键值对上设置一个***(单调递增),然后通过网络发送大于SNP 3 snap _ end的***来进行优化。 然而,KV存储仍必须从SNP 1’的开头读取,才能到达snp3的snap_end。

这种方法的另一个缺点是,最新的键值对在形成不可变的增量快照之前不能被复制。 这增加了**发送到副本的延迟。

增量快照很好地复制了相当大的一批项目,也就是说,吞吐量高,但延迟也高。

时间点快照是动态创建的快照,也就是说,当新数据和更新写入KV存储时,如果复制客户端请求数据,存储会创建快照。 这意味着要接收最新的键值,复制副本不需要等待在源上创建快照。

高吞吐量和低延迟的主从式repli

时间点快照可以在仅附加的KV存储上非常快速地创建(低延迟),并且最适合于项目的内存中稳态复制。 我们所说的稳定状态是指所有复制客户端几乎都已捕获了源。

这个模型要求每个键值对都有一个单调递增的***。 A point-in-time snapshot is defined by the tuple {start_seqno, end_seqno}.

假设源具有从***0到100的键值对,并且复制客户端R1请求复制数据。 从***0到100的键值对作为快照(时间点快照)发送到R1,并且对应于客户端R1的光标C1被标记在存储上。 稍后,假设商店已经追加了20多个千伏对,并且具有120的最高***。 如果另一个客户端R2请求数据,从***0到120的键值对作为快照发送到R2,并且对应于客户端R2的光标C2被标记在存储上。 当更多的数据被附加到存储中时,比如直到***150,客户端r 1可以在从101到150的连续时间点快照中获得高达150的数据,客户端R2可以在从121到150的连续时间点快照中获得高达150的数据。 请注意,光标C1和C2对于从客户R1和R2之前离开的地方快速重新开始很重要。 当游标移动时,***小于任何游标被标记的***的键值对可以被压缩而没有任何读写争用,或者从内存中移除(在持久KV存储上的内存内数据复制的情况下)。

高吞吐量和低延迟的主从式repli

时间点快照非常适合稳态复制,因为复制客户端可以按需创建自己的快照,因此无需等待快照的创建。 因此,客户可以通过尽快发送的最新键值对,非常快地赶上来源。 此外,如果压缩在其连续快照之间运行,客户端不需要重新开始,源也不需要保留增量快照供所有客户端读取。

慢速客户端和滞后(延迟)客户端不能很好地使用时间点快照。 由于我们无法在没有读写争用的情况下压缩或从内存中弹出***小于光标***且***最少的键值对,因此一个速度较慢的客户端会降低所有其他客户端的时间点快照创建速度,并增加内存使用量。

时间点快照是快速复制最新项目的好方法;即低延迟但低吞吐量。

通过在分区复制期间根据需要在增量快照模式或时间点快照模式之间动态切换,可以实现高吞吐量和低延迟。

当复制客户端连接到源时,它最初会以高吞吐量获得增量快照,以便很快赶上源,从而达到稳定状态。 然后,复制切换到时间点快照模式,从而客户端可以以非常低的延迟获得最新的项目。 如果由于某种原因,客户端变得很慢,复制将回落到增量增量快照模式,以减少任何不健康的内存使用增加。 一旦慢速客户端赶上源并再次达到稳定状态,复制就会切换到时间点快照模式。

在仅附加模式下,删除总是附加在存储的末尾。 在使用快照的复制中,附加删除对于反映在所有副本中删除的**至关重要。 然而,我们不能永远保留删除,因为它们是存储内存的开销。 因此,最终,删除必须被清除。

但是清除删除会影响慢速复制ients and can sometimes break incremental replication, especially on clients that have not caught up o the snapshot where a delete has been purged. Such clients might have to rebuild all the snapshots from beginning to get a copy that is consistent with the source.

Hard failures can lead to different data branches across replicas. These branches can be reconciled and all replicas can have the same consistent copies eventually. There are protocols and algorithms to do this and they very well intersect with the snapshotting world. However, we will keep those out of the scope of this article. 本文仅讨论没有硬故障时的复制和快照方案。

重复数据消除是指删除快照中相同**的重复版本,仅保留该快照中**的最终版本。 重复数据消除的主要目的是减少内存使用。

压缩期间,重复数据消除在增量快照中完成。 在时间点快照中,重复数据消除可以在压缩期间完成,也可以在添加项目时完成。 将重复数据消除与时间点快照结合使用会带来额外的复杂性,例如在读取时间点快照时无法写入,以及在客户端源发生故障时无法恢复。 如前所述,对这种故障场景的讨论超出了本文的范围。

NoSQL键值/文档存储库Couchbase从磁盘动态选择增量快照或从内存选择时间点快照,为复制客户端和其他数据消费者提供服务。 来自磁盘的增量快照也使用单调递增的***从客户端停止的地方恢复,以减少网络流量。

Couchbase还执行重复数据删除、检测、节点故障处理和数据分支协调,提供比复制客户端(索引、全文搜索)更复杂的数据客户端,执行缓存和分区,以及提供高可用性、高性能和内存优先的数据平台。

在主从复制中,增量快照有利于复制一批项目,因此具有高吞吐量。 时间点快照有利于稳态复制,从而提供低延迟。 通过根据情况需要使用其中一种,我们可以获得高吞吐量和低延迟的主从复制。