bulkload确实不太合适,可以采取下面方法:
1. 避免region split
不得不说,region split是提升写性能的一大障碍。减少region split次数可以从两方面入手。首先是预分配region。
预分配region
不在此重复region split的原理,请参见http://blog.sina.com.cn/s/blog_9cee0fd901018vu2.html。按数据量,row key的规则预先设计并分配好region,可以大幅降低region split的次数, 甚至不split。这点非常重要。
适当提升hbase.hregion.max.filesize
提升region的file容量也可以减少split的次数。具体的值需要按照你的数据量,region数量,row key分布等情况具体考量。一般来说,3~4G是不错的选择。
2. 均匀分布每个Region Server的写压力
之前也提到了RPC Handler的概念。好的Data Loader需要保证每个RPC Handlder都有活干,每个handler忙,但不至超载。注意region的压力不能过大,否则会导致反复重试,并伴有超时异常(可以提高超时的时间设置)。
如何保证每个Region Server的压力均衡呢?这和region 数量,startKey设计, client数据插入顺序有关。
一般来说,简单的数据插入程序应该是多线程实现。让每个线程负责一部分的row key范围,而row key范围又和region相关,所以可以在数据插入时,程序控制每个region的压力,不至于有些region闲着没事干。
那么,如何设计row key呢?举个比较实际的例子,如果有张HBase表来记录每天某城市的通话记录, 常规思路下的row key是由电话号码 + yyyyMMddHHmmSS + ... 组成。按电话号码的规律来划分region。但是这样很容易导致数据插入不均匀(因为电话通话呈随机性)。但是,如果把电话号码倒序,数据在region层面的分布情况就大有改观。
3. 分布式的数据插入程序
HBase客户端在单节点上运行,即使使用多线程,也受限于单节点的硬件资源,写入速度不可能很快。典型的思路是将客户端部署在多个节点上运行,提高写的并发度。MapReduce是个很好的选择。使用MapReduce把写入程序分布到集群的各个节点上,并在每个mapper中运行多线程的插入程序。这样可以很好的提高写并发度。
注意,不要使用reducer。mapper到reducer需要走网络,受限于集群带宽。其次,实际的应用场景一般是用户从关系型数据库中导出了文本类型的数据,然后希望能把导出的数据写到HBase里。在这种情况下,需要小心谨慎地设计和实现file split逻辑。
4. HBase Client太慢?BulkLoad!
请拿出HBase的API读读,HFileOutputFomart里有个叫configureIncrementalLoad的方法。API是这么介绍的:
- Configure a MapReduce Job to perform an incremental load into the given table. This
-
- Inspects the table to configure a total order partitioner
-
- Uploads the partitions file to the cluster and adds it to the DistributedCache
-
- Sets the number of reduce tasks to match the current number of regions
-
- Sets the output key/value class to match HFileOutputFormat's requirements
-
- Sets the reducer up to perform the appropriate sorting (either KeyValueSortReducer or PutSortReducer)
-
- The user should be sure to set the map output value class to either KeyValue or Put before running this function.
复制代码
这是HBase提供的一种基于MapReduce的数据导入方案,完美地绕过了HBase Client(上一节的分布式插入方法也是用mapreduce实现的,不过本质上还是用hbase client来写数据)
网上有不少文章叙述了使用命令行方式运行BulkLoad,比如
但是,不得不说,实际生产环境上很难使用这种方式。毕竟源数据不可能直接用来写HBase。在数据迁移的过程中会涉及到数据清洗、整理归并等许多额外的工作。这显然不是命令行可以做到的事情。按照API的描述, 可行的方案是自定义一个Mapper在mapper中清洗数据,Mapper的输出value为HBase的Put类型,Reducer选用PutSortReducer。然后使用HFileOutputFormat#configureIncrementalLoad(Job, HTable);解决剩余工作。
不过,这种实现也存在局限性。毕竟Mapper到Reducer比较吃网络。
参考:hbase写入太慢,BulkLoad不太实用,该如何调优
|