This is an automated email from the ASF dual-hosted git repository.
wutao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-pegasus-website.git
The following commit(s) were added to refs/heads/master by this push:
new 1c83f50 update post of duplication design doc
1c83f50 is described below
commit 1c83f500d6e7cb9ad10e39df9e3b8696aef5efcb
Author: neverchanje <[email protected]>
AuthorDate: Tue Aug 18 19:41:50 2020 +0800
update post of duplication design doc
---
_administration/duplication.md | 4 +-
_posts/2019-06-09-duplication-design.md | 66 ++++++++++++++++++++++-----------
2 files changed, 46 insertions(+), 24 deletions(-)
diff --git a/_administration/duplication.md b/_administration/duplication.md
index 9286356..c337bc0 100644
--- a/_administration/duplication.md
+++ b/_administration/duplication.md
@@ -211,9 +211,9 @@ set_dup_fail_mode <app_name> <dupid> <slow|skip>
dup_lagging_write_threshold_ms = 10000
```
--
`replica*eon.replica_stub*dup.pending_mutations_count`:当前有多少写堆积在源集群,且尚未复制。如果一切正常,该监控项会稳定维持在某个的值上下。当某个环节出现故障时,往往会有大量的写堆积,该值会持续上涨。
+-
`replica*eon.replica_stub*dup.pending_mutations_count`:当前有多少写堆积在源集群,且尚未复制。如果一切正常,该监控项会稳定维持在某个值上下。当热备份的某个环节出现故障时,往往会有大量的写堆积,该值会持续上涨。
--
`replica*eon.replica_stub*dup.load_file_failed_count`:源集群读取日志文件的失败次数。日志文件的读取是热备份的关键环节,如果该环节因某种原因出现故障,则会导致日志长时间无法被复制。
+-
`replica*eon.replica_stub*dup.load_file_failed_count`:源集群读取日志文件的失败次数。日志文件的读取是热备份的关键环节,如果该环节因某种原因出现故障,则会导致热备份被阻塞。
## 热备份的元信息
diff --git a/_posts/2019-06-09-duplication-design.md
b/_posts/2019-06-09-duplication-design.md
index 8cde183..c4abc8e 100644
--- a/_posts/2019-06-09-duplication-design.md
+++ b/_posts/2019-06-09-duplication-design.md
@@ -33,10 +33,11 @@ author: 吴涛
| +---+ | | |
+-------+ +-------+
dead alive
-
-只用两机房,使用 raft 协议进行进行跨机房同步依然无法避免机房故障时的停服。(5节点同理)
```
+如上图可看到,只用两机房,使用 raft 协议进行进行跨机房同步依然
+无法避免机房故障时的停服。(5节点同理)
+
```
+---+ +---+
| A | | B |
@@ -47,22 +48,22 @@ author: 吴涛
| | pegasus A <----------> pegasus B | |
| +--------------+ +--------------+ |
+--------------------------------------------------+
-
- 虽然是各写一个机房,但理想情况下 A B 都能读到所有的数据。
-
```
+如上图可看到,虽然是各写一个机房,但理想情况下 A B 都能读到所有的数据。
+机房故障时,原来访问A集群的客户端可以切换至B集群。
+
## 架构选择
即使同样是做方案 3 的集群间异步同步,业内的做法也有不同:
-1.
**各集群单副本**:这种方案考虑到多集群已存在冗余的情况下,可以减少单集群内的副本数,同时既然一致性已没有保证,大可以索性脱离一致性协议,完全依赖于稳定的集群间网络,保证即使单机房宕机,损失的数据量也是仅仅几十毫秒内的请求量级。考虑机房数为
5 的时候,如果每个机房都是 3 副本,那么全量数据就是 3*5=15 副本,这时候简化为各集群单副本的方案就是几乎最自然的选择。
+1.
**各集群单副本**:这种方案考虑到多集群已存在冗余的情况下,可以减少单集群内的副本数。同时既然一致性已没有保证,大可以索性脱离一致性协议,完全依赖于稳定的集群间网络,保证即使单机房宕机,损失的数据量也是仅仅几十毫秒内的请求量级。考虑机房数为
5 的时候,如果每个机房都是 3 副本,那么全量数据就是 3*5=15 副本,这时候简化为各集群单副本的方案就是几乎最自然的选择。
-2.
**同步工具作为外部依赖使用**:跨机房同步自然是尽可能不影响服务是最好,所以同步工具可以作为外部依赖部署,单纯访问节点磁盘的日志(WAL)并转发日志。这个方案对日志
GC 有前提条件,即**日志不可以在同步完成前被删除**,否则就丢数据了,但存储服务日志的 GC
是外部工具难以控制的。所以可以把日志强行保留一周以上,但缺点是磁盘空间的成本较大。同步工具作为外部依赖的优点在于稳定性强,不影响服务,缺点在于对服务的控制能力差,很难处理一些琐碎的一致性问题(后面会讲到),**难以实现最终一致性**。
+2.
**同步工具作为外部依赖使用**:跨机房同步自然是尽可能不影响服务是最好,所以同步工具可以作为外部依赖部署,单纯访问节点磁盘的日志(WAL)并转发日志。这个方案对日志
GC 有前提条件,即**日志不可以在同步完成前被删除**,否则就丢数据了。但存储服务日志的 GC
是外部工具难以控制的,所以可以把日志强行保留一周以上,但缺点是磁盘空间的成本较大。同步工具作为外部依赖的优点在于稳定性强,不影响服务,缺点在于对服务的控制能力差,很难处理一些琐碎的一致性问题(后面会讲到),**难以实现最终一致性**。
-3. **同步工具嵌入到服务内部**:这种做法在工具稳定前会有一段阵痛期,即工具的稳定性影响服务的稳定性。但实现的灵活性肯定是最强的。
+3. **同步工具嵌入到服务内部**:对应到 Pegasus 则是将热备份功能集成至 ReplicaServer
中。这种做法在工具稳定前会有一段阵痛期,即工具的稳定性会影响服务的稳定性。但实现的灵活性较优,同时易于部署,不需要部署额外的服务。
-最初 Pegasus 的热备份方案借鉴于 HBase Replication,基本只考虑了第三种方案。而事实证明这种方案更容易保证 Pegasus
存储数据不丢的属性。
+最初 Pegasus 的热备份方案借鉴于 HBase Replication,基本只考虑了第三种方案。而确实这种方案更容易保证 Pegasus 数据的一致性。
## 基本概念
@@ -107,7 +108,7 @@ pegasus 的热备份以表为粒度。支持单向和双向的复制。为了运
### duplicate_rpc
-如上图所示,每个 replica (这里特指每个分片的 primary,注意 secondary 不负责热备份复制)独自复制自己的 private log
到远端,replica 之间互不影响。复制直接通过 pegasus client 来完成。每一条写入 A 的记录(如 set / multiset)都会通过
pegasus client 复制到 B。为了将热备份的写与常规写区别开,我们这里定义 ***duplicate_rpc*** 表示热备写。
+如上图所示,每个 replica (这里特指每个分片的 primary,注意 secondary 不负责热备份复制)独自复制自己的 private log
到目的集群,replica 之间互不影响。数据复制直接通过 pegasus client 来完成。每一条写入 A 的记录(如 set /
multiset)都会通过 pegasus client 复制到 B。为了将热备份的写与常规写区别开,我们这里定义 ***duplicate_rpc***
表示热备写。
A->B 的热备写,B 也同样会经由三副本的 PacificA 协议提交,并且写入 private log 中。
@@ -143,11 +144,15 @@ timetag = timestamp << 8u | cluster_id << 1u | delete_tag;
### confirmed_decree
-热备份同时也需要容忍在 replica 主备切换下复制的进度不会丢失,例如当前 replica1 复制到日志
decree=5001,此时发生主备切换,我们不想看到 replica1 从 0 开始,所以为了能够支持 ***断点续传***,我们引入
***confirmed_decree***。replica 定期向 meta 汇报当前进度(如 confirmed_decree=5001),一旦 meta
将该进度持久化至 zookeeper,当 replica 故障恢复时即可安全地从 confirmed_decree=5001 重新开始热备份。
+热备份同时也需要容忍在 ReplicaServer 主备切换下复制的进度不会丢失,例如当前 replica1 复制到日志
decree=5001,此时发生主备切换,我们不想看到 replica1 从 0 开始,所以为了能够支持 ***断点续传***,我们引入
***confirmed_decree***。
+
+ReplicaServer 定期向 MetaServer 汇报当前热备份的进度(如 confirmed_decree=5001),一旦 MetaServer
将该进度持久化至 Zookeeper,当 。ReplicaServer 故障恢复时即可安全地从 confirmed_decree=5001 重新开始热备份。
## 流程
-热备份相关的元信息首先会记录至 meta server 上,replica server 通过 ***duplication sync***
定期同步元信息,包括各个分片的 confirmed_decree。
+### 1. 热备份元信息同步
+
+热备份相关的元信息首先会记录至 MetaServer 上,ReplicaServer 通过 ***duplication sync***
定期同步元信息,包括各个分片的 confirmed_decree。
```
+----------+ add dup +----------+
@@ -161,7 +166,11 @@ timetag = timestamp << 8u | cluster_id << 1u | delete_tag;
+-----------+
```
-每个 replica 首先读取 private log,为了限制流量,每次只会读入一个日志块而非一整个日志文件。每一批日志统一传递给
`mutation_duplicator` 进行发送。mutation_duplicator 是一个接口类,目前只实现用 pegasus client
将日志分发至目标集群,未来如有需求也可接入 HBase 等系统。
+### 2. 热备份日志复制
+
+每个 replica 首先读取 private log,为了限制流量,每次只会读入一个日志块而非一整个日志文件。每一批日志统一传递给
`mutation_duplicator` 进行发送。
+
+`mutation_duplicator` 是一个可插拔的接口类。我们目前只实现用 pegasus client 将日志分发至目标集群,未来如有需求也可接入
HBase 等系统,例如将 Pegasus 的数据通过热备份实时同步到 HBase 中。
```
+----------------------+ 2
@@ -177,21 +186,29 @@ timetag = timestamp << 8u | cluster_id << 1u | delete_tag;
+-----------------+ +-----------------------------+ 3
```
-每个日志块的一批写中可能有多组 hashkey,不同的 hashkey 可以并行分发而不会影响正确性,从而可以提高热备份效率。而如果 hashkey
相同,例如 set<hashkey="h", sortkey="s1", value="v1"> 与 set<hashkey="h",
sortkey="s2", value="v2"> 有先后关系,则它们必须串行依次发送。
+每个日志块的一批写中可能有多组 hashkey,不同的 hashkey 可以并行分发而不会影响正确性,从而可以提高热备份效率。而如果 hashkey
相同,例如:
+
+1. `Set: hashkey="h", sortkey="s1", value="v1"`
+2. `Set: hashkey="h", sortkey="s2", value="v2"`
+
+这两条写有先后关系,则它们必须串行依次发送。
-当前我们的策略是每一个日志块的所有写发完毕后,再重复读日志块,发日志的过程。往后可能再做优化。
+1. `Set: hashkey="h1", sortkey="s1", value="v1"`
+2. `Set: hashkey="h2", sortkey="s2", value="v2"`
+
+这两条写是不相干的,它们无需串行发送。
## 日志完整性
在引入热备份之前,Pegasus
的日志会定期被清理,无用的日志文件会被删除(通常日志的保留时间为5分钟)。但在引入热备份之后,如果有被删除的日志还没有被复制到远端集群,两集群就会数据不一致。我们引入了几个机制来保证日志的完整性,从而实现两集群的最终一致性:
-### GC Delay
+### 1. GC Delay
-Pegasus 认为 `last_durable_decree` 之后的日志即可被删除回收(Garbage Collected),因为它们已经被持久化至
rocksdb 的 sst files 中,即使宕机重启数据也不会丢失。但考虑如果热备份的进度较慢,我们则需要延后 GC,保证数据只有在
`confirmed_decree` 之后的日志才可被 GC。
+Pegasus 先前认为 `last_durable_decree` 之后的日志即可被删除回收(Garbage Collected),因为它们已经被持久化至
rocksdb 的 sst files 中,即使宕机重启数据也不会丢失。但考虑如果热备份的进度较慢,我们则需要延后 GC,保证数据只有在
`confirmed_decree` 之后的日志才可被 GC。
当然我们也可以将日志 GC 的时间设置的相当长,例如一周,因为此时数据必然已复制到远端集群(什么环境下复制一条日志需要超过 1
周时间?)。最终我们没有选择这种方法。
-### Broadcast confirmed_decree
+### 2. Broadcast confirmed_decree
虽然 primary 不会 GC 那些未被热备的日志,但 secondary 并未遵守这一约定,这些丢失日志的 secondary 有朝一日也会被提拔为
primary,从而影响日志完整性。所以 primary 需要将 confirmed_decree 通过组间心跳(group check)的方式通知
secondary,保证它们不会误删日志。
@@ -211,7 +228,7 @@ Pegasus 认为 `last_durable_decree` 之后的日志即可被删除回收(Garb
这里有一个问题:由于 secondary 滞后于 primary 了解到热备份正在进行,所以在创建热备份后,secondary
有一定概率误删日志。这是一个已知的设计bug。我们会在后续引入新机制来修复该问题。
-### Replica Learn Step Back
+### 3. Replica Learn Step Back
当一个 replica 新加入3副本组中,由于它的数据滞后于 primary,它会通过 ***replica learn***
来拷贝新日志以跟上组员的进度。此时从何处开始拷贝日志(称为 `learn_start_decree`)就是一个问题。
@@ -261,15 +278,18 @@ learner
我们假设 learner 已经持有 [251, 400] 的日志,下一步 learnee 将会复制 [301, 800] 的日志,与 learner
数据为空的情况相同。新的日志集将会把旧的日志集覆盖。
-### Sync is_duplicating to every replica
+### 4. Sync is_duplicating to every replica
不管是考虑 GC,还是考虑 learn,我们都需要让每一个 replica 知道“自己正在进行热备份”,因为普通的表不应该考虑 GC
Delay,也不应该考虑在 learn 的过程中补齐未热备份的日志,只有热备份的表需要额外考虑这些事情。所以我们需要向所有 replica
同步一个标识(`is_duplicating`)。
这个同步不需要考虑强一致性:不需要在 `is_duplicating` 的值改变时强一致地通知所有 replica。但我们需要保证在 replica
learn 的过程中,该标识能够立刻同步给 learner。因此,我们让这个标识通过 config sync 同步。
-### Apply Learned State
+### 5. Apply Learned State
+
+原先流程中,learner 收到 [21-60] 之间的日志后首先会放入 learn/ 目录下,然后简单地重放每一条日志并写入 rocksdb。Learn
流程完成后这些日志即丢弃。
+如果没有热备份,该流程并没有问题。但考虑到热备份,如果 learner 丢弃 [21-60] 的日志,那么热备份的日志完整性就有问题。
-原先 learner 收到 [21-60] 之间的日志后首先会放入 learn/ 目录下,然后简单地重放每一条日志并写入
rocksdb,并不会写入日志中。为了保证日志完整性,我们会将 learn/ 目录 rename 至 plog 目录,替代之前所有的日志。
+为了解决这一问题,我们会将 learn/ 目录 rename 至 plog 目录,替代之前所有的日志。
```
+----+
@@ -310,3 +330,5 @@ learner
| 21 |
+----+
```
+
+通过整合上述的几个机制,Pegasus实现了在热备份过程中,数据不会丢失。
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]