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