Hbase 1.2.0 over Hadoop 3.0.0

在Hadoop 3.0.0 HDFS Erasure Code上构建HBASE服务

背景

  • 场景:实时访问冷数据存储
  • 需要提供给使用方hbase服务,尽量低成本,使用snappy压缩 + HDFS Erasure Code

具体版本信息

  • Hadoop: 3.0.0-alpha1-SNAPSHOT,源码编译
  • Hbase: 1.2.0-cdh5.7.0,基于cdh源码包更改

环境搭建

  1. 替换Hbase目录下Hadoop相关jar文件为3.0.0-alpha1-SNAPSHOT版本 (hadoop-client在新版本Hadoop中已经不存在)

  2. 设置Hdfs根目录使用Erasure Code (最终方案是设置的指定table目录使用Erasure Code)

    1
    hdfs erasurecode -setPolicy /

问题1:MetricsServlet NoClassDefFoundError

现象

start-hbase.sh启动后,regionserver报错退出,出错日志:

1
2
3
4
5
Caused by: java.lang.NoClassDefFoundError: org/apache/hadoop/metrics/MetricsServlet
at org.apache.hadoop.hbase.http.HttpServer.addDefaultServlets(HttpServer.java:679)
at org.apache.hadoop.hbase.http.HttpServer.initializeWebServer(HttpServer.java:549)
at org.apache.hadoop.hbase.http.HttpServer.<init>(HttpServer.java:499)
at org.apache.hadoop.hbase.http.HttpServer.<init>(HttpServer.java:103)

相关的Hbase代码在:

1
2
hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
679: addServlet("metrics", "/metrics", MetricsServlet.class);
1
2
3
4
5
6
7
8
9
10
11
/**
* Add default servlets.
*/
protected void addDefaultServlets() {
// set up default servlets
addServlet("stacks", "/stacks", StackServlet.class);
addServlet("logLevel", "/logLevel", LogLevel.Servlet.class);
addServlet("metrics", "/metrics", MetricsServlet.class);
addServlet("jmx", "/jmx", JMXJsonServlet.class);
addServlet("conf", "/conf", ConfServlet.class);
}

原因

  • Hadoop trunk上已经把org/apache/hadoop/metrics删除了,取而代之的是org/apache/hadoop/metrics2,但是还是没有MetricsServlet这个类
  • Hbase jira上有相关的issue,对应的Hdfs的issue在这里

解决方案

去掉metric,编译1.2.0-cdh5.7.0版本Hbase源码

1
2
3
4
5
6
7
8
9
10
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
@@ -676,7 +676,6 @@ public class HttpServer implements FilterContainer {
// set up default servlets
addServlet("stacks", "/stacks", StackServlet.class);
addServlet("logLevel", "/logLevel", LogLevel.Servlet.class);
- addServlet("metrics", "/metrics", MetricsServlet.class);
addServlet("jmx", "/jmx", JMXJsonServlet.class);
addServlet("conf", "/conf", ConfServlet.class);
}

编译步骤:

1
2
3
4
git clone https://github.com/sel-fish/hbase-1.2.0-cdh5.7.0.git
cd hbase-1.2.0-cdh5.7.0
mvn -DskipTests -Dsnappy -Denforcer.skip=true package assembly:single install
find * -name hbase*.gz

问题2:DFSStripedOutputStream.hflush Unsupport

现象

start-hbase.sh启动后,regionserver报错退出,出错日志:

1
2
3
4
5
6
2016-05-25 15:09:24,874 ERROR [regionserver/mogunode/x.x.x.x:60020] regionserver.HRegionServer: Failed init
java.lang.UnsupportedOperationException
at org.apache.hadoop.hdfs.DFSStripedOutputStream.hflush(DFSStripedOutputStream.java:769)
at org.apache.hadoop.fs.FSDataOutputStream.hflush(FSDataOutputStream.java:117)
at org.apache.hadoop.hbase.regionserver.wal.ProtobufLogWriter.sync(ProtobufLogWriter.java:171)
at org.apache.hadoop.hbase.regionserver.wal.FSHLog.preemptiveSync(FSHLog.java:661)

原因

Erasure Code使用的DFSStripedOutputStream类还未实现hflush方法

1
2
3
4
5
6
hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSStripedOutputStream.java
@Override
public void hflush() {
throw new UnsupportedOperationException();
}

解决方案

WAL还是用3副本,data目录中指定表目录使用Erasure Code

  1. 重启dfs,删除之前所有文件
  • 启动Hbase
  • 创建表XDSnapshot
  • 设置表对应目录数据布局方式为Erasure Code
1
hdfs erasurecode -setPolicy /hbase/data/default/XDSnapshot

测试

测试只关注空间使用

YCSB写入数据

50万数据,value为2K,配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
recordcount=500000
operationcount=500000
workload=com.yahoo.ycsb.workloads.CoreWorkload
fieldlength=256
fieldcount=8
readallfields=true
readproportion=1
updateproportion=0
scanproportion=0
insertproportion=0
requestdistribution=zipfian
columnfamily=cf
table=XDSnapshot

数据目录配置

对应表数据所在的目录已经设置为使用Erasure Code:

1
2
$ hdfs erasurecode -getPolicy /hbase/data/default/XDSnapshot
ErasureCodingPolicy=[Name=RS-DEFAULT-6-3-64k, Schema=[ECSchema=[Codec=rs-default, numDataUnits=6, numParityUnits=3]], CellSize=65536 ]

空间使用

Hbase目录占用空间大小:

1
2
$ du -sh *
1.3G hbase

Hbase目录下各文件夹占用空间详情:

1
2
3
4
5
6
$ du -sh *
4.0K archive
1.2G data
4.0K MasterProcWALs
4.0K oldWALs
120M WALs

按照data目录1.2G,副本系数为1.5,其它空间副本系数为3来计算,预算占用DFS空间为:

1
1.2G * 1.5 + (1.3G - 1.2G) * 3 = 2.1 GB

实际DFS使用空间:

1
DFS Used: 2.07 GB (0.05%)

符合预期

其它

之前在测试的时候,hdfs-client有报错:

1
2
hdfs.DFSOutputStream: Failed to get block location for parity block, index=7
hdfs.DFSOutputStream: Failed to get block location for parity block, index=8

代码在这里:

1
2
hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSStripedOutputStream.java
481: LOG.warn("Failed to get block location for parity block, index=" + i);

原因是测试环境只有7台datanode,使用的默认Erasure Code算法是RS-DEFAULT-6-3-64k,机器数目最好是9台以上,否则,数据块和校验块会落在同一节点上降低容灾级别。