HBase表数据倾斜治理_Zookeeper在HBase中的作用

HBase表数据倾斜治理_Zookeeper在HBase中的作用

1.Zookeeper基础概念

1.1集群角色

在zookeeper中有leader、follower、observer三种角色。一个zookeeper集群在一个时刻只会有一个leader,由所有follower参与的选举产生。

1.2会话(Session)

在ZooKeeper中,一个客户端连接是指客户端和ZooKeeper服务器之间的TCP长连接。

ZooKeeper对外的服务端口默认是2181,客户端启动时,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测和服务器保持有效的会话,也能够向ZooKeeper服务器发送请求并接受响应,同时还能通过该连接接收来自服务器的Watch事件通知。

Session的SessionTimeout值用来设置一个客户端会话的过期时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,在SessionTimeout时间内重新连接上Zookeeper集群任意一台服务器,那么之前创建的会话仍然有效。

1.3数据节点(ZNode)

ZooKeeper中的数据节点可以理解为即是文件,又是文件夹。每个ZNode不仅本身可以写数据,还可以有下一级文件或目录。

ZooKeeper将所有数据存储在内存中,数据模型是传统的树型结构(ZNode Tree)。由斜杠(/)进行分割的路径,就是一个ZNode。如/hbase/master,其中hbase和master都是ZNode。每个ZNode上都会保存自己的数据内容,同时会保存一系列属性信息。

在ZooKeeper中,ZNode可以分为持久节点和临时节点两类。持久节点是指在创建后只能主动删除的ZNode。临时节点用于与客户端会话绑定,一旦客户端会话失效,该客户端创建的临时节点都会被自动删除。

1.4事件监听器(Watcher)

ZooKeeper允许客户端在指定节点上注册一些Watcher,当该节点发生一些特定事件时,ZooKeeper服务端会将事件通知到感兴趣的客户端上去。该机制是ZooKeeper实现分布式协调服务的重要支撑。

2.Zookeeper在HBase中的应用

2.1 HMaster主备切换

hbase集群启动后,所有的master节点与zookeeper建立会话之后会竞争创建/hbase/master节点,该节点是一个锁节点,只有一个master可以创建成功。其他竞争失败的master会创建/hbase/backup-masters/[host-name]节点,同时在/hbase/master节点下创建一个watcher。一旦当前Master挂了,/hbase/master节点被销毁后,其他备选master就会重新竞争。

这就是HBase的HA原理,竞争成功的master状态为active,失败的master状态为standby。master节点数量必须大于1才能实现HA。

2.2系统容错

hbase集群启动后,所有的regionserver节点与zookeeper建立会话之后都会创建/hbase/rs/[host-name]节点,active master会在/hbase/rs路径下的所有节点注册监听器,当有regionserver宕机失效时,active master会将对该regionserver的数据请求路由到其他节点,同时增加一份region备份。

2.3元数据Region管理

hbase中使用一张特殊的.META表来保存集群中所有region的位置信息,该表的结构类似于B树,且不会split。

.META表同样被保存在一个region当中,zookeeper是用永久节点/hbase/meta-region-server来记录.META表所在region的位置信息。

2.4 Region管理

region发生分裂、合并、迁移等变更时都会发出下线(offline)信号,变更完成后都会发出重新上线(online)信号,regionserver通过zookeeper将这些信息发送给master,master会更新region在.META表和内存中的状态信息,使得下线期间的数据不可被访问。

2.5分布式SplitWAL任务管理

regionserver当中正在写入的数据会被保存在WAL预写日志(hlog)当中,如果还没有写入完成时regionserver宕机了,master会将该hlog按照region切分,分配给多台regionserver来共同完成。

master会在zookeeper上创建/hbase/SplitWAL节点,将“哪个regionserver恢复哪个region数据”这样的信息以列表的形式存放到该节点上,对应的regionserver自行到节点上领取任务并将恢复结果记录在该节点上。

2.6 Replication管理

zookeeper可以支持hbase主集群和从集群的数据同步,从而支持hbase集群的容灾和备份。

开启hbase的replication功能之后,主集群回将数据实时的推送给各个从集群,保证数据的最终一致性。

  • /hbase/replication/state节点当中保存布尔值,表示replication功能功能是否启用;
  • /hbase/replication/peers节点当中存储所有从集群的基本信息和状态,每个从集群会分配一个唯一id;
  • /hbase/replication/rs节点用来存储主集群上所有有效的regionserver、hlog文件的索引和hlog文件读取的偏移量。

2.7 hbase-site.xml中zookeeper的配置

hbase-site.xml当中与zookeeper相关的主要配置属性如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<property>
<name>hbase.zookeeper.quorum</name>
<value>hdp101,hdp102,hdp103</value>
</property>
<property>
<name>hbase.zookeeper.property.clientPort</name>
<value>2181</value>
</property>
<property>
<name>zookeeper.znode.parent</name>
<value>/hbase-unsecure</value>
</property>
<property>
<name>zookeeper.session.timeout</name>
<value>90000</value>
</property>

hbase.zookeeper.quorum用于配置zookeeper集群地址,hbase.zookeeper.property.clientPort用于配置zookeeper集群对外提供服务的端口,hbase通过上述两个参数加起来去连接zookeeper。

zookeeper.znode.parent用于配置hbase在使用zookeeper时,创建数据节点的根目录。通过该配置可以实现多个hbase集群公用同一个zookeeper集群,只需要为这些hbase集群配置不同的根目录,如/hbase-unsecure1、/hbase-unsecure2。

zookeeper.session.timeout用于配置regionserver会话的过期时间。

3.Zookeeper端口切换问题

当服务器的2181端口被占用且无法杀死时,我们只能将zookeeper对外的服务端口配置为其他端口,如配置为2182。

3.1 hbase配置

我们需要在hbase安装目录的./conf/hbase-site.xml文件添加如下配置:

1
2
3
4
<property>
<name>hbase.zookeeper.property.clientPort</name>
<value>2182</value>
</property>

因为如果不添加该配置,默认zookeeper端口值为2181,那么hbase会连接不上zookeeper,导致hbase启动失败。

3.2 java代码中连接hbase

当我们编写java代码对hbase表数据进行增删改查时,我们首先要建立hbase连接对象,那么jvm实际上是通过zookeeper来连接hbase的。

jvm会从hbase-common-jar包中去取hbase-site.xml文件,加载其中的hbase默认配置信息,其中就包括hbase.zookeeper.property.clientPort=2181,按照该端口号去连接hbase会导致连接失败,错误信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
org.apache.hadoop.hbase.client.RetriesExhaustedException: Can't get the locations
at org.apache.hadoop.hbase.client.RpcRetryingCallerWithReadReplicas.getRegionLocations(RpcRetryingCallerWithReadReplicas.java:312)
at org.apache.hadoop.hbase.client.ScannerCallableWithReplicas.call(ScannerCallableWithReplicas.java:156)
at org.apache.hadoop.hbase.client.ScannerCallableWithReplicas.call(ScannerCallableWithReplicas.java:60)
at org.apache.hadoop.hbase.client.RpcRetryingCaller.callWithoutRetries(RpcRetryingCaller.java:210)
at org.apache.hadoop.hbase.client.ClientScanner.call(ClientScanner.java:326)
at org.apache.hadoop.hbase.client.ClientScanner.nextScanner(ClientScanner.java:301)
at org.apache.hadoop.hbase.client.ClientScanner.initializeScannerInConstruction(ClientScanner.java:166)
at org.apache.hadoop.hbase.client.ClientScanner.<init>(ClientScanner.java:161)
at org.apache.hadoop.hbase.client.HTable.getScanner(HTable.java:797)
at org.apache.hadoop.hbase.client.MetaScanner.metaScan(MetaScanner.java:193)
at org.apache.hadoop.hbase.client.MetaScanner.metaScan(MetaScanner.java:89)
at org.apache.hadoop.hbase.client.MetaScanner.allTableRegions(MetaScanner.java:324)
at org.apache.hadoop.hbase.client.HRegionLocator.getAllRegionLocations(HRegionLocator.java:89)
at org.apache.hadoop.hbase.util.RegionSizeCalculator.init(RegionSizeCalculator.java:94)
at org.apache.hadoop.hbase.util.RegionSizeCalculator.<init>(RegionSizeCalculator.java:81)
at org.apache.hadoop.hbase.mapreduce.TableInputFormatBase.getSplits(TableInputFormatBase.java:256)
at org.apache.hadoop.hbase.mapreduce.TableInputFormat.getSplits(TableInputFormat.java:237)
at org.apache.hadoop.mapreduce.JobSubmitter.writeNewSplits(JobSubmitter.java:301)
at org.apache.hadoop.mapreduce.JobSubmitter.writeSplits(JobSubmitter.java:318)
at org.apache.hadoop.mapreduce.JobSubmitter.submitJobInternal(JobSubmitter.java:196)
at org.apache.hadoop.mapreduce.Job$10.run(Job.java:1290)
at org.apache.hadoop.mapreduce.Job$10.run(Job.java:1287)

我们需要在代码中手动将hbase.zookeeper.property.clientPort配置为2182,才可以正确连接到hbase,代码如下:

1
2
3
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.property.clientPort", "2182");
Connection conn = ConnectionFactory.createConnection(conf);