Replication:复制,指的是持续的将同一份数据拷贝到多个地方进行存储,是各类存储系统中常见而又重要的一个概念,能够指数据库中主库和从库的复制,也能够指分布式集群中多个集群之间的复制,还能够指分布式系统中多个副本之间的复制。它的难点在于数据一般是不断变化的,须要持续的将变化也反映到多个数据拷贝上,并保证这些拷贝是彻底一致的。一般来讲,数据复制到多个拷贝上有以下好处:html
多个备份提升了数据的可靠性数据库
经过主从数据库/主备集群之间的复制,来分离OLTP和OLAP请求apache
提升可用性,即便在单副本挂掉的状况下,依然能够有其余副原本提供读写服务架构
可扩展,经过增长副原本服务更多的读写请求负载均衡
跨地域数据中心之间的复制,Client经过读写最近的数据中心来下降请求延迟异步
HBase中的Replication指的是主备集群间的复制,用于将主集群的写入记录复制到备集群。HBase目前共支持3种Replication,分别是异步Replication,串行Replication,和同步Replication。async
若是想把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指的是:对于某个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的核心思路就是在写入主集群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]中进行优化。
除了上述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…
本文首发于公众号“小米云技术”,转载请标明出处,点击查看原文连接。