HBase Replication详解

Replication:复制,指的是持续的将同一份数据拷贝到多个地方进行存储,是各类存储系统中常见而又重要的一个概念,能够指数据库中主库和从库的复制,也能够指分布式集群中多个集群之间的复制,还能够指分布式系统中多个副本之间的复制。它的难点在于数据一般是不断变化的,须要持续的将变化也反映到多个数据拷贝上,并保证这些拷贝是彻底一致的。一般来讲,数据复制到多个拷贝上有以下好处:html

  • 多个备份提升了数据的可靠性数据库

  • 经过主从数据库/主备集群之间的复制,来分离OLTP和OLAP请求apache

  • 提升可用性,即便在单副本挂掉的状况下,依然能够有其余副原本提供读写服务架构

  • 可扩展,经过增长副原本服务更多的读写请求负载均衡

  • 跨地域数据中心之间的复制,Client经过读写最近的数据中心来下降请求延迟异步


HBase中的Replication指的是主备集群间的复制,用于将主集群的写入记录复制到备集群。HBase目前共支持3种Replication,分别是异步Replication,串行Replication,和同步Replication。async

异步Replication

若是想把HBase的Replication搞清楚,首先须要了解下HBase的架构。HBase集群是由一组进程组成的,进程按角色分为Master和RegionServer,其中Master负责DDL操做,好比建表、删表,而RegionServer负责DML操做,好比数据的读写操做等。从数据视图上讲,HBase中的Table会按Range切分为多个Region,而后由不一样的RegionServer来负责对外提供服务。分布式

RegionServer的内部则主要有BlockCache,MemStore和WAL等几部分组成,须要注意的是每一个Region的每一个Column Family有本身独享的MemStore,可是BlockCache和WAL则是多个Region共享的。WAL(Write-ahead logging)是数据库中的经常使用技术,全部的修改在写入数据库以前都须要持久化到WAL中,从而确保了在出现故障的时候,能够从WAL中回放出已经成功写入的数据。ide

HBase中的Replication也是基于WAL的,其在主集群的每一个RegionServer进程内部起了一个叫作ReplicationSource的线程来负责Replication,同时在备集群的每一个RegionServer内部起了一个ReplicationSink的线程来负责接收Replication数据。ReplicationSource记录须要同步的WAL队列,而后不断读取WAL中的内容,同时能够根据Replication的配置作一些过滤,好比是否要复制这个表的数据等,而后经过replicateWALEntry这个Rpc调用来发送给备集群的RegionServer,备集群的ReplicationSink线程则负责将收到的数据转换为put/delete操做,以batch的形式写入到备集群中。性能

由于是后台线程异步的读取WAL并复制到备集群,因此这种Replication方式叫作异步Replication,正常状况下备集群收到最新写入数据的延迟在秒级别。

串行Replication

串行Replication指的是:对于某个Region来讲,严格按照主集群的写入顺序复制到备集群,其是一种特殊的Replication。同时默认的异步Replication不是串行的,主要缘由是Region是能够移动的,好比HBase在进行负载均衡时移动Region。假设RegionA首先在RegionServer1上,而后其被移动到了RegionServer2上,因为异步Replication是存在延迟的,因此RegionA的最后一部分写入记录尚未彻底复制到备集群上。在Region移动到RegionServer2以后,其开始接收新的写入请求,并由RegionServer2来复制到备集群,因此在这个时候RegionServer1和RegionServer2会同时向备集群进行复制,并且写入记录复制到备集群的顺序是不肯定的。

如上图所示这种极端状况下,还会致使主备集群数据的不一致。好比RegionServer1上最后一个未同步的写入操做是Put,而RegionA被移动到RegionServer2上的第一个写入操做是Delete,在主集群上其写入顺序是先Put后Delete,若是RegionServer2上的Delete操做先被复制到了备集群,而后备集群作了一次Major compaction,其会删除掉这个Delete marker,而后Put操做才被同步到了备集群,由于Delete已经被Major compact掉了,这个Put将永远没法被删除,因此备集群的数据将会比主集群多。

解决这个问题的关键在于须要确保RegionServer2上的新写入操做必须在RegionServer1上的写入操做复制完成以后再进行复制。因此串行Replication引入了一个叫作Barrier的概念,每当Region open的时候,就会写入一个新的Barrier,其值是Region open时读到的最大SequenceId加1。SequenceId是HBase中的一个重要概念,每一个Region都有一个SequenceId,其随着数据写入严格递增,同时SequenceId会随着每次写入操做一块儿写入到WAL中。因此当Region移动的时候,Region会在新的RegionServer从新打开,这时就会写入一个新的Barrier,Region被移动屡次以后,就会写入多个Barrier,来将Region的写入操做划分红为多个区间。同时每一个Region都维护了一个lastPushedSequenceId,其表明这个Region当前推送成功的最后一个写操做的SequenceId,这样就能够根据Barrier列表和lastPushedSequenceId来判断WAL中的一个写入操做是否可以复制到备集群了。


以上图为例,Pending的写入记录就须要等待lastPushedSequenceId推到Barrier2以后才能开始复制。因为每一个区间之间只会有一个RegionServer来负责复制,因此只有和lastPushedSequenceId在同一个区间内的RegionServer才能进行复制,并在复制成功后不断更新lastPushedSequenceId,而在lastPushedSequenceId以后各个区间的RegionServer则须要等待lastPushedSequenceId被推到本身区间的起始Barrier,而后才能开始复制,从而确保了Region的写入操做能够严格按照主集群的写入顺序串行的复制到备集群。

同步Replication

同步Replication是和异步Replication对称的概念,其指的是主集群的写入操做必须被同步的写入到备集群中。异步Replication的最大问题在于复制是存在延迟的,因此在主集群整集群挂掉的状况下,备集群是没有已经写入的完整数据的,对于一致性要求较高的业务来讲,是不能把读写彻底切到备集群的,由于在这个时候可能存在部分最近写入的数据没法从备集群读到。因此同步Replication的核心思路就是在写入主集群WAL的同时,在备集群上写入一份RemoteWAL,只有同时在主集群的WAL和备集群的RemoteWAL写入成功了,才会返回给Client说写入成功。这样当主集群挂掉的时候,即可以在备集群上根据Remote WAL来回放出来主集群上全部写入记录,从而确保备集群和主集群数据的一致。

须要注意的是,同步Replication是在异步Replication的基础之上的,也就是说异步Replication的复制链路还会继续保留,同时增长了新的写Remote WAL的步骤。对于具体的实现细节来讲,首先是增长了一个Sync replication state的概念,其总共有三个状态, 分别是Active,Downgrade Active和Standby。这几个状态的转换关系以下图所示,Standby在提主的时候须要首先提高为Downgrade Active,而后才能提高为Active。可是Active是能够直接降级为Standby的。目前这个状态是保存在ReplicationPeerConfig中的, 其表示一个集群在这个ReplicationPeer中处于哪一个状态。

而后实现了一个DualAsyncFSWAL来同时写主集群的WAL和备份集群的Remote WAL。写WAL的操做是对于HDFS的rpc请求,其会有三种结果: 成功,失败或者超时。当超时的时候,对于HBase来讲结果是不肯定的,即数据有可能成功写入到WAL或Remote WAL里了,也有可能没有。只有同时写成功或者同时写失败的时候,主集群和备集群才会有同样的WAL,若是是主集群写WAL成功,写Remote WAL失败或者超时,这时候主集群WAL里的数据就有可能比备集群的Remote WAL多。相反若是写备集群Remote WAL成功了,而主集群的WAL写失败或者超时了,备集群的Remote WAL里的数据就有可能比主集群多。当两边都超时的时候, 就不肯定那边多了。因此同步复制的关键就在于在上述状况下,如何确保主备集群数据的最终一致。即在切换主备集群的时候,Client应该始终从主备集群看到一致的数据。并且在主备没有达到一致的中间状态时,须要一些限制来确保Client无法读到这种中间不一致的结果。因此总结一下就是主备集群最终一致,但对于Client来讲是强一致,即成功写入的数据不管主备集群都要必定能读到。具体的实现细节能够参考HBaseCon Asia 2018:HBase at Xiaomi[1]。



Async Replication

Sync Replication

Read Path

No affect

No affect

Write Path

No affect

Write extra remote WAL

Network bandwidth

100% for async replication.

100% for async + 100% for remote WAL.

Storage space

No affect

Need extra space for remote WAL

Eventual Consistency

No if active cluster crashed

Yes

Availability

Unavailable when master crash

Few time for waiting replay remote log.

Operational Complexity

Simple

More complex, Need to transition cluster state by hand or your services.

对比异步复制来看,同步复制主要是影响的写路径,从咱们的测试结果上来看,大概会有14%的性能降低,后续计划在HBASE-20422[2]中进行优化。

自定义Replication Endpoint

除了上述3种Replication以外,HBase还支持插件式的Replication Endpoint,能够自定义Replication Endpoint来实现各类各样的功能。具体到小米来讲,咱们实现了能够在不一样表之间的Replication Endpoint,好比能够将主集群的表A复制到备集群的表B,其应用场景有集群迁移,Namespace更换或者表名更换等。同时为了实现Point-in-time Recovery,咱们作了能够同步数据到小米消息队列Talos的Replication Endpoint,当出现须要恢复某个时间点t1的场景时,能够首先找到冷备中距离t1最近的一个的Snapshot进行恢复,而后将消息队列中的数据来回放到t1时间点,从而作到Point-in-time Recovery。最后,咱们还作了相似DynamoDB Stream的功能[3],将用户表的修改日志复制到用户本身的消息队列中,而后用户能够依赖这份数据来作一些流式的数据处理。此外,HBase Read Replica功能,目前的一种实现方案就是依赖HBase Replication,经过插件式的Replication Endpoint将主Replica的数据复制到其余Read Replica中,详见HBASE-10070[4]。

以上就是HBase中各类各样的Replication,若有错误,欢迎指正。

同时欢迎你们在业务场景中按需进行使用,你们也能够根据本身的特殊场景自定义新的Replication Endpoint,并欢迎贡献到社区。

参考连接:
https://www.slideshare.net/MichaelStack4/hbaseconasia2018-track13-hbase-at-xiaomi
https://jira.apache.org/jira/browse/HBASE-20422
https://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/Streams.html
https://issues.apache.org/jira/browse/HBASE-10070
mapr.com/blog/in-dep…

本文首发于公众号“小米云技术”,转载请标明出处,点击查看原文连接

相关文章
相关标签/搜索