HBase-0.94.4+bulkload 海量数据载库神器

06 Jun 2013

bulkload 是以 Mapreduce 的方式将制定格式文件装载到 hbase 中,采用线程池,对于海量数据装载入 hbase 非常有用

bulkload 可以导入已存在的 hbase 表中(更新数据),若存在和已有表中相同 rowkey 的记录,会更新表中此 rowkey 记录的数据

应用环境

将 Mysql 中一个5000万条数据的 tableA 插入到一个已存在的 hbase tableB 中,两个表在逻辑结构上一致,具体字段有出入

平台环境

  • Hadoop-1.0.4
  • HBase-0.94.4

Step1 配置

因为 bulkload 是运行 hadoop 程序,不会自动查找 hbase 的 conf 路径,因此找不到 hbase 的环境变量。需要做如下配置:

hadoop-env.sh

配置 $HADOOP_HOME/conf/hadoop-env.sh 文件,修改 HADOOP_CLASSPATH:

export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:$HBASE_HOME/conf:$HBASE_HOME/hbase-0.94.4.jar:$HBASE_HOME/hbase-0.94.4-tests.jar:$HBASE_HOME/lib/guava-11.0.2.jar:$HBASE_HOME/lib/zookeeper-3.4.5.jar

core-site.xml

配置 $HADOOP_HOME/conf/core-site.xml,将 zookeeper 的配置写入 core-site.xml 中,内容与 $HADOOP_HOME/conf/hbase-site.xml 中保持一致:

<property>
    <name>hbase.zookeeper.quorum</name>
    <!--value值按照$HADOOP_HOME/conf/hbase-site.xml中配置的写 -->
    <value>hadoop-namenode,hadoop-datanode1,hadoop-datanode2</value>
</property>

重启 hadoop 和 hbase

Step2 准备文件

文件格式是每条记录一行,默认以 \t 制表符隔开,例如生成文件 ~/user 如下:

100000001	张三	18
100000002	李四	22
100000003	王五	24

Step3 上传文件

将文件 user 上传至 HDFS 上,目录为 /bulkload

$ hadoop fs -put ~/user /bulkload

Step4 将文件包装成 HFile

$ hadoop jar $HBASE_HOME/hbase-0.94.4.jar  importtsv \
> -Dimporttsv.separator=";" \  #指定输入文件的分隔符为;,分隔符只能为单字节
> -Dimporttsv.bulk.output=/output \   # 输出hfile到/output,/output必须不存在
> -Dimporttsv.columns=HBASE_ROW_KEY,cf:USER_NAME,cf:AGE \ #源文件的第一列为rowkey,第二列为cf:USER_NAMEM,第三列为cf:AGE
> user /bulkload   #导入hbase的user表中,输入文件存放在/bulkload

下面可以分为两个步骤,也可以一次完成

Style1 两步走

按照上面指定 Dimporttsv.bulk.output=/output,此时只是将 HFile 输出到了 HDFS 上的 /output 下。还需要将 HFile 转移到对应的 region 中,这一步只是 mv,所以相当快

# 将/output下的HFile刷入user表中,若user表不存在则会被新建
$hadoop jar $HBASE_HOME/hbase-0.94.4.jar completebulkload /output user

Style2 一步走

可以在上面的命令中不指定 Dimporttsv.bulk.output=/output,则会一步将数据导入到 user 表中

Problem1

若你指定的分隔符 Dimporttsv.separator 为多字节,则会报错,默认分隔符是 \t:

java.lang.IllegalArgumentException: TsvParser only supports single-byte separators
        at com.google.common.base.Preconditions.checkArgument(Preconditions.java:88)
        at org.apache.hadoop.hbase.mapreduce.ImportTsv$TsvParser.<init>(ImportTsv.java:88)
        at org.apache.hadoop.hbase.mapreduce.ImportTsv$TsvImporter.setup(ImportTsv.java:218)
        at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:142)
        at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:621)
        at org.apache.hadoop.mapred.MapTask.run(MapTask.java:305)
        at org.apache.hadoop.mapred.Child.main(Child.java:170)

Problem2

如果你像我一样使用的是 hbase-0.94.* 版本,在 Style1 中执行 completebulkload 命令时会报错,这是 hbase-0.94.* 的一个 bug:

java.util.concurrent.ExecutionException: java.lang.IllegalStateException: The value of the hbase.metrics.showTableName conf option has not been specified in SchemaMetrics
        at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
        at java.util.concurrent.FutureTask.get(FutureTask.java:111)
        at org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles.groupOrSplitPhase(LoadIncrementalHFiles.java:333)
        at org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles.doBulkLoad(LoadIncrementalHFiles.java:232)
        at org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles.run(LoadIncrementalHFiles.java:699)
        at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:65)
        at org.apache.hadoop.util.ToolRunner.run(ToolRunner.java:79)
        at org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles.main(LoadIncrementalHFiles.java:704)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.apache.hadoop.util.ProgramDriver$ProgramDescription.invoke(ProgramDriver.java:68)
        at org.apache.hadoop.util.ProgramDriver.driver(ProgramDriver.java:139)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.apache.hadoop.hbase.mapreduce.Driver.main(Driver.java:51)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.apache.hadoop.util.RunJar.main(RunJar.java:156)

解决方案1: 采用 Style2,这样会跳过这个 bug

解决方案2: 更新 hbase

$ cd $HBASE_HOME/
# 下载 HBASE-4802.patch
$ wget https://issues.apache.org/jira/secure/attachment/12503953/HBASE-4802.patch
$ patch -p0 < HBASE-4802.patch
# 用maven重新进行编译
$ mvn package -Dmaven.test.skip.exec=true

然后再执行 completebulkload 命令通过^^

>>利用BulkLoad向Hbase插入数据过程详解
>>hbase bulk-load
>>bulk-load装载hdfs数据到hbase小结
>>使用HBASE的BULK LOAD

Fork me on GitHub