一、环境准备
1、要求两套集群安装相同的CDH版本,如都为CDH4或者CDH5,不限定小版本
2、要求两套集群网络互通,至少保证Master HBase到Salve HBase网络是可达的
二、开启HBase replication功能
启用HBase复制功能,如下图:
image.png
在修改配置后, 需要重启HBase集群,建议采用滚动重启的方式重启HBase各个实例。
三、配置hosts
在Master集群所有机器的hosts中配置Slave集群的hostname与ip
需要注意的地方有:
1、至少配置slave集群的HBase和zk的hostname
2、hostname必须与slave集群内部使用的hostname一致
四、增加peer
在建立peer前,首先简单的介绍下hbase关于peer的原理以及操作命令,建议参照hbase复制详解。上文介绍得非常的详细,在这里不累述,主要介绍如何正确的关闭一个peer。
首先回顾下创建一个peer的过程:
- 新增一个peer
add_peer 'peerID','zkIp:2181:/hbase'
其中peerID不可与已经存在的peer冲突,可通过list_peers查看已有的peer;
如果配置多个zk:zk1,zk2:2181:/hbase
- 将peerID=1的peer应用表t1
对于已经建立的表,需要开启REPLICATION_SCOPE属性,如:
disable 't1'
alter 't1',{NAME=>"cf", REPLICATION_SCOPE=>"1"}
enable 't1'
对于REPLICATION_SCOPE属性,一开始我一度以为其值=peerID,并且在设置时并未报错。经过查看源码,发现REPLICATION_SCOPE的取值范围为(0,1,2),源码在HConstants类中定义了三种模式:
/**
* 本地模式,当前数据不会被复制
* Scope tag for locally scoped data.
* This data will not be replicated.
*/
public static final int REPLICATION_SCOPE_LOCAL = 0;
/**
* 全局模式,此数据会被复制给所有peer
* Scope tag for globally scoped data.
* This data will be replicated to all peers.
*/
public static final int REPLICATION_SCOPE_GLOBAL = 1;
/**
* 连续模式,数据会按照队列ID的顺序复制给所有的peer
* Scope tag for serially scoped data
* This data will be replicated to all peers by the order of sequence id.
*/
public static final int REPLICATION_SCOPE_SERIAL = 2;
对于0和1这两个取值还比较好理解,而2并不能简单的从上面得到解释
在hbase-default.xml中找到如下解释:
image.png 目前来看serially模式,大概为按照Master集群的HLog写入顺序在Slave集群实现同样的顺序,当前这会增加集群的延时,同时需要将hbase.assignment.usezk 和 hbase.master.distributed.log.replay两项配置设置为false才能启用。
- 将peer应用到表
set_peer_tableCFs 'peerId,'table:cf;table1:cf1,cf2'
append_peer_tableCFs 'peerId,'table:cf;table1:cf1,cf2'
许多的博文里都有提到这两个命令,但是却没有指出两者的区别,这阅读源码(HBase1.2)后,得到这样的解释:
1、append_peer_tableCFs
增加peer到表,粒度到列簇。
2、set_peer_tableCFs
更新tableCFs到peer。假如事先执行了append_peer_tableCFs ‘peerId,’table:cf;table1:cf1,cf2’,再执行set_peer_tableCFs ‘peerId’,’table1:cf1’,那么table1:cf2将从当前peer中移除,而append_peer_tableCFs会保留已有的列簇。两个方式实现的源码如下:
public static UpdateReplicationPeerConfigRequest buildUpdateReplicationPeerConfigRequest(
String peerId, ReplicationPeerConfig peerConfig) {
UpdateReplicationPeerConfigRequest.Builder builder = UpdateReplicationPeerConfigRequest
.newBuilder();
builder.setPeerId(peerId);
builder.setPeerConfig(ReplicationSerDeHelper.convert(peerConfig));
return builder.build();
}
public static void appendTableCFsToReplicationPeerConfig(
Map<TableName, ? extends Collection<String>> tableCfs, ReplicationPeerConfig peerConfig) {
Map<TableName, List<String>> preTableCfs = peerConfig.getTableCFsMap();
if (preTableCfs == null) {
peerConfig.setTableCFsMap(tableCfs);
} else {
for (Map.Entry<TableName, ? extends Collection<String>> entry : tableCfs.entrySet()) {
TableName table = entry.getKey();
Collection<String> appendCfs = entry.getValue();
if (preTableCfs.containsKey(table)) {
List<String> cfs = preTableCfs.get(table);
if (cfs == null || appendCfs == null || appendCfs.isEmpty()) {
preTableCfs.put(table, null);
} else {
Set<String> cfSet = new HashSet<String>(cfs);
cfSet.addAll(appendCfs);
preTableCfs.put(table, Lists.newArrayList(cfSet));
}
} else {
if (appendCfs == null || appendCfs.isEmpty()) {
preTableCfs.put(table, null);
} else {
preTableCfs.put(table, Lists.newArrayList(appendCfs));
}
}
}
}
}
至此,可以验证在Master集群中往表t1写入数据,观察Slave集群是否成功备份。
上面介绍了开启一个表的复制,需要开启REPLICATION_SCOPE属性,然后应用某一个peer,是否会存在这TABLE_CFS和REPLICATION_SCOPE配置有什么区别的疑问?
REPLICATION_SCOPE 是让 replicationSource 开启的 WAL reader 能够看到 log 里哪些 WALEntry 能被 replication , TABLE_CFS 则是在 WALEntry 中过滤出真正需要复制的 cf 数据
五、删除peer
一开始以为删除一个peer的方式很简单,只需运行命令
remove_peer 'peerId'
但是,观察到regionServer日志依旧在尝试连接Slave集群,并且在Master集群的t1表写入数据后,Slave集群依旧同步到了。
image.png
显示,这样并没有成功的删除表的复制链路。如下图:
image.png 在zk的/hbase/replication/peers/下只有一个peer,但是在rs/region的config里还存在被remove后的peer。一开始我采用在zk里删除rs/region下错误的peer的方式,但是不久后又被创建,猜测这份配置已经被hbase加载到了内存中。
一种解决方式为:在remove peer后,将table的REPLICATION_SCOPE属性修改为0。当然,在生产环境对表进行disable也是非常不可取的。
另外一种解决方式:先remove_peer_tableCFs删除表对应的peer信息,再删除peer,一定要记住顺序。最后将表的REPLICATION_SCOPE修改为。修改
我目前并没有测试成功,但是查看社区是有人这么完成了的。就是使用enable_table_replication命令。
image.png