在 pegasus 中,跨机房同步又被成为 热备份,或 duplication,简称 dup。这一功能的主要目的是保证 数据中心级别的可用性。当业务需要保证服务与数据能够容忍机房故障时,可以考虑使用此功能。

此外,当 Pegasus 客户端在多机房分布时,时常会遇到跨机房访问 Pegasus 服务带来的高延时问题,这时我们可以将 Pegasus 的服务与客户端部署在相同的机房内,客户端可以只读写本地机房的服务,然后由热备份功能将写同步到各个机房上。这种做法既能保证各个机房都有完整数据,又能避免跨机房的延时开销。

  1. client client client
  2. + + +
  3. +---------v-------+ +--------v--------+ +------v-----------+
  4. | | | | | |
  5. | pegasus-beijing <---> pegasus-tianjin <---> pegasus-shanghai |
  6. | | | | | |
  7. +----------^------+ +-----------------+ +---------^--------+
  8. | |
  9. +------------------------------------------+

我们能够做到一主一备(single-master),也能提供多机房多主(multi-master),用户可以根据需要进行配置。

这里需要注意的是,跨机房同步是异步的数据复制,并非完全实时。与单机房不同,我们不提供跨机房 read-after-write 的一致性保证。目前在跨机房网络健康的环境下,数据延时大概在 10s 左右,即 A 机房的写数据大概在 10s 后会写入 B 机房。

Get Started

假设我们有两个 pegasus 集群 bjsrv-accounttjsrv-account 分别位于北京与天津的两个机房内,表 account_xiaomi 由于存储了极其关键的用户帐号数据,需要能够在双集群保证可用,所以我们为它实施热备份:

  1. ./run.sh shell -n bjsrv-account
  2. Type "help" for more information.
  3. Type "Ctrl-D" or "Ctrl-C" to exit the shell.
  4. The cluster name is: bjsrv-account
  5. The cluster meta list is: ***
  6. >>> ls
  7. app_id status app_name
  8. 12 AVAILABLE account_xiaomi
  9. >>> add_dup account_xiaomi tjsrv-account
  10. Success for adding duplication [appid: 12, dupid: 1535008534]
  11. >>> query_dup account_xiaomi
  12. duplications of app [account_xiaomi] are listed as below:
  13. | dup_id | status | remote cluster | create time |
  14. | 1535008534 | DS_START | tjsrv-account | 2018-08-23 15:15:34 |

通过 add_dup 命令,bjsrv-account 集群的表 account_xiaomi 将会近实时地把数据复制到 tjsrv-account 上,这意味着,每一条在北京机房的写入,最终都一定会复制到天津机房。

热备份使用日志异步复制的方式来实现跨集群的同步,可与 mysql 的 binlog 复制和 hbase replication 类比。每个 replica 单独发送自己的日志到远端集群上,replica 之间互不干扰,保证了。

热备份的两集群的表名需要保持一致,但 partition 的个数不需要相同。例如用户可以建表如下:

  1. >>> cc bjsrv-account
  2. >>> create account_xiaomi -p 128
  3. >>>
  4. >>> cc tjsrv-account
  5. >>> create account_xiaomi -p 32

线上表开启热备

有时一个线上表可能在设计之初未考虑到跨机房同步的需求,而在服务一段时间后,才决定进行热备份。此时我们需要进行全量数据拷贝,例如将 bj 已有的全部数据复制到 tj。因为是线上表,拷贝过程中不可以停止服务,同时拷贝过程中的写增量数据也不能丢

面对这个需求,我们的思路是:首先 bj 保留从此刻开始的所有写增量(即 wal),将 bj 的全量快照(冷备份)上传至 HDFS / xiaomi-FDS 上,然后恢复到 tj。此后 bj 开启热备份,并重放此前堆积的写增量,复制到远端 tj 机房。

如何保留写增量?pegasus 如此进行操作:

  • 首先使用 add_dup [—freezed/-f] 表示不进行日志复制,它的原理就是阻止当前日志 GC(log compaction)。该操作 必须最先执行,否则无法保证数据完整性。
  1. >>> cc bjsrv-account
  2. >>> add_dup account_xiaomi tjsrv-account --freezed
  • 虽然不进行复制,但每个分片都会记录当前确认点(confirmed_decree)(初始为 private_log->max_committed_decree),并持久化到 meta server 上。注意需等待所有的 replica 都将当前确认点更新至 meta server 后,才可进行下一步操作,这是该功能正确性的前提。 confirme_decree 值为 -1 即表示该分片的确认点尚未同步。
  1. >>> query_dup_detail account_xiaomi 1535008534
  2. >>> {"dupid":1548442533,"status":"DS_START","remote":"c4srv-feedhistory","create_ts":1548442533763,"progress":[{"pid":0,"confirmed":-1},{"pid":1,"confirmed":276444333},{"pid":2,"confirmed":-1},{"pid":3,"confirmed":-1},{"pid":4,"confirmed":-1},{"pid":5,"confirmed":-1},{"pid":6,"confirmed":-1},{"pid":7,"confirmed":279069949},{"pid":8,"confirmed":-1}}
  3. >>> query_dup_detail account_xiaomi 1535008534
  4. >>> {"dupid":1548442533,"status":"DS_START","remote":"c4srv-feedhistory","create_ts":1548442533763,"progress":[{"pid":0,"confirmed":276444111},{"pid":1,"confirmed":276444333},{"pid":2,"confirmed":276444332},{"pid":3,"confirmed":276444222},{"pid":4,"confirmed":276444111},{"pid":5,"confirmed":276444377},{"pid":6,"confirmed":276444388},{"pid":7,"confirmed":279069949},{"pid":8,"confirmed":276444399}}
  • 使用冷备份将数据快照上传至远端存储,再使用恢复功能在 tjsrv-account 恢复该表。示例命令如下:
  1. # 立刻对表(app_id = 12)进行冷备
  2. ./run.sh shell -n bjsrv-account
  3. >>> add_backup_policy -p dup_transfer -b fds_wq -a 12 -i 86400 -s 12:01 -c 1
  4. # 耐心等待备份生成
  5. >>> query_backup_policy -p dup_transfer
  6. policy_info:
  7. name : dup_transfer
  8. backup_provider_type : fds_wq
  9. backup_interval : 86400s
  10. app_ids : {12}
  11. start_time : 12:01
  12. status : enabled
  13. backup_history_count : 1
  14. backup_infos:
  15. [1]
  16. id : 1541649698875
  17. start_time : 2018-11-08 12:01:38
  18. end_time : 2018-11-08 12:03:51
  19. app_ids : {60}
  20. # 在天津机房恢复表
  21. ./run.sh shell -n tjsrv-account
  22. >>> restore_app -c bjsrv-account -p dup_transfer -a account_xiaomi -i 12 -t 1541649698875 -b fds_wq
  23. # 开启日志复制
  24. >>> start_dup account_xiaomi <dupid>
  25. # 至此热备份已经完全可用。

start_dup 时,热备份任务会从之前的确认点开始复制,这样我们就保证了写增量的完整性。

另外需注意的是,由于写增量的长时间堆积,一时可能有大量日志复制,热备份流量会突增,从而导致服务不稳定。因此,我们需要在远端机房设置限流(write throttling)。

  1. >>> get_app_envs
  2. get app envs succeed, count = 7
  3. =================================
  4. replica.write_throttling = 30000*delay*100,40000*reject*200
  5. =================================

元信息存储

热备份的元信息会经由 meta server 持久化于 zookeeper 上,其存储路径如下:

  1. <cluster_root> <app_id> <dupid>
  2. | | |
  3. | | |
  4. [zk: 127.0.0.1:22181(CONNECTED) 0] get /pegasus/bjsrv-account/0.0.x.x/apps/1/duplication/1537336970
  5. {"remote":"tjsrv-account","status":"DS_START","create_timestamp_ms":1537336970483}

配置项

  • duplication_enabled:如果遇到紧急情况想要手动关闭热备份,可以将该项设置为 false,默认为 true。

  • 开启热备份的集群必须配置远端机房的具体地址

  1. [pegasus.clusters]
  2. tjsrv-account = 127.0.0.1:51601,127.0.0.1:51601
  • 热备份要求 allow_non_idempotent_write 必须为 false(默认值)。因为开启热备份的集群 不支持 “幂等操作” 如 CHECK_AND_MUTATE, CHECK_AND_SET,INCR 等。

  • 如果有多机房同时写的需求,配置 verify_timetag 需修改为 true :

  1. [pegasus.server]
  2. verify_timetag = true
  • 热备份的集群需要登记其 cluster_id:
  1. # Configuration for cluster_id.
  2. # This is required for every cluster that enables duplication.
  3. [duplication-group]
  4. tjsrv-account = 1
  5. bjsrv-account = 2

我们在每条数据前都会加上 timestamp+cluster_id 的前缀,timestamp 即数据写到 pegasus 的时间戳,cluster_id 即上面 duplication-group 中所配置的,tjsrv 的 cluster_id 为 1,bjsrv 的 cluster_id 为 2。

cluster_id 具有两个作用:

一旦出现写冲突,例如 tjsrv 和 bjsrv 同时写 key "user_1",系统首先会检查两次写的时间戳,以时间戳大的为最终值。当极罕见地遇到时间戳相同的情况时,以 cluster_id 大的为最终值。使用这种机制我们可以保证两集群的最终值一定相同。

cluster_id 的另一个作用就是保证热备份不会

相关监控项

监控项描述
dup.log_read_in_bytes_rate
dup.log_mutations_read_rate
dup.shipped_size_in_bytes_rate
dup.time_lag(ms)
dup.shipped_ops
dup.failed_shipping_ops
duplicated_put_qps
duplicated_remove_qps
duplicated_multi_put_qps
duplicated_multi_remove_qps

Known Limitations

  • 热备份暂时不建议两机房同时写一份数据。在我们的业务经验看来,通常这是可以接受的。用户可以将数据均分在 tjsrv 和 bjsrv 两机房内,热备份能保证当任一机房宕机,只有数秒的数据丢失(假设机房之间网络稳定)。