hbase海量数据导入

最近有个需求要对mysql的全量数据迁移到hbase,虽然hbase的设计非常利于高效的读取,但是它的compaction实现对海量数据写入造成非常大的影响,数据到一定量之后,就开始抽风。 
分析hbase的实现,不管其运行的机制,其最终存储结构为分布式文件系统中的hfile格式。 
刚好hbase的源代码中提供一个HFileOutputFormat类,分析其源代码可以看到: 
Java代码  收藏代码
  1. /** 
  2.  * Copyright 2009 The Apache Software Foundation 
  3.  * 
  4.  * Licensed to the Apache Software Foundation (ASF) under one 
  5.  * or more contributor license agreements.  See the NOTICE file 
  6.  * distributed with this work for additional information 
  7.  * regarding copyright ownership.  The ASF licenses this file 
  8.  * to you under the Apache License, Version 2.0 (the 
  9.  * "License"); you may not use this file except in compliance 
  10.  * with the License.  You may obtain a copy of the License at 
  11.  * 
  12.  *     http://www.apache.org/licenses/LICENSE-2.0 
  13.  * 
  14.  * Unless required by applicable law or agreed to in writing, software 
  15.  * distributed under the License is distributed on an "AS IS" BASIS, 
  16.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  17.  * See the License for the specific language governing permissions and 
  18.  * limitations under the License. 
  19.  */  
  20. package org.apache.hadoop.hbase.mapreduce;  
  21.   
  22. import java.io.IOException;  
  23. import java.util.Map;  
  24. import java.util.TreeMap;  
  25.   
  26. import org.apache.hadoop.conf.Configuration;  
  27. import org.apache.hadoop.fs.FileSystem;  
  28. import org.apache.hadoop.fs.Path;  
  29. import org.apache.hadoop.hbase.HConstants;  
  30. import org.apache.hadoop.hbase.KeyValue;  
  31. import org.apache.hadoop.hbase.io.ImmutableBytesWritable;  
  32. import org.apache.hadoop.hbase.io.hfile.Compression;  
  33. import org.apache.hadoop.hbase.io.hfile.HFile;  
  34. import org.apache.hadoop.hbase.regionserver.StoreFile;  
  35. import org.apache.hadoop.hbase.util.Bytes;  
  36. import org.apache.hadoop.mapreduce.RecordWriter;  
  37. import org.apache.hadoop.mapreduce.TaskAttemptContext;  
  38. import org.apache.hadoop.mapreduce.lib.output.FileOutputCommitter;  
  39. import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;  
  40. import org.mortbay.log.Log;  
  41.   
  42. /** 
  43.  * Writes HFiles. Passed KeyValues must arrive in order. 
  44.  * Currently, can only write files to a single column family at a 
  45.  * time.  Multiple column families requires coordinating keys cross family. 
  46.  * Writes current time as the sequence id for the file. Sets the major compacted 
  47.  * attribute on created hfiles. 
  48.  * @see KeyValueSortReducer 
  49.  */  
  50. public class HFileOutputFormat extends FileOutputFormat<ImmutableBytesWritable, KeyValue> {  
  51.   public RecordWriter<ImmutableBytesWritable, KeyValue> getRecordWriter(TaskAttemptContext context)  
  52.   throws IOException, InterruptedException {  
  53.     // Get the path of the temporary output file   
  54.     final Path outputPath = FileOutputFormat.getOutputPath(context);  
  55.     final Path outputdir = new FileOutputCommitter(outputPath, context).getWorkPath();  
  56.     Configuration conf = context.getConfiguration();  
  57.     final FileSystem fs = outputdir.getFileSystem(conf);  
  58.     // These configs. are from hbase-*.xml  
  59.     final long maxsize = conf.getLong("hbase.hregion.max.filesize"268435456);  
  60.     final int blocksize = conf.getInt("hfile.min.blocksize.size"65536);  
  61.     // Invented config.  Add to hbase-*.xml if other than default compression.  
  62.     final String compression = conf.get("hfile.compression",  
  63.       Compression.Algorithm.NONE.getName());  
  64.   
  65.     return new RecordWriter<ImmutableBytesWritable, KeyValue>() {  
  66.       // Map of families to writers and how much has been output on the writer.  
  67.       private final Map<byte [], WriterLength> writers =  
  68.         new TreeMap<byte [], WriterLength>(Bytes.BYTES_COMPARATOR);  
  69.       private byte [] previousRow = HConstants.EMPTY_BYTE_ARRAY;  
  70.       private final byte [] now = Bytes.toBytes(System.currentTimeMillis());  
  71.   
  72.       public void write(ImmutableBytesWritable row, KeyValue kv)  
  73.       throws IOException {  
  74.         long length = kv.getLength();  
  75.         byte [] family = kv.getFamily();  
  76.         WriterLength wl = this.writers.get(family);  
  77.         if (wl == null || ((length + wl.written) >= maxsize) &&  
  78.             Bytes.compareTo(this.previousRow, 0this.previousRow.length,  
  79.               kv.getBuffer(), kv.getRowOffset(), kv.getRowLength()) != 0) {  
  80.           // Get a new writer.  
  81.           Path basedir = new Path(outputdir, Bytes.toString(family));  
  82.           if (wl == null) {  
  83.             wl = new WriterLength();  
  84.             this.writers.put(family, wl);  
  85.             if (this.writers.size() > 1throw new IOException("One family only");  
  86.             // If wl == null, first file in family.  Ensure family dir exits.  
  87.             if (!fs.exists(basedir)) fs.mkdirs(basedir);  
  88.           }  
  89.           wl.writer = getNewWriter(wl.writer, basedir);  
  90.           Log.info("Writer=" + wl.writer.getPath() +  
  91.             ((wl.written == 0)? """, wrote=" + wl.written));  
  92.           wl.written = 0;  
  93.         }  
  94.         kv.updateLatestStamp(this.now);  
  95.         wl.writer.append(kv);  
  96.         wl.written += length;  
  97.         // Copy the row so we know when a row transition.  
  98.         this.previousRow = kv.getRow();  
  99.       }  
  100.   
  101.       /* Create a new HFile.Writer. Close current if there is one. 
  102.        * @param writer 
  103.        * @param familydir 
  104.        * @return A new HFile.Writer. 
  105.        * @throws IOException 
  106.        */  
  107.       private HFile.Writer getNewWriter(final HFile.Writer writer,  
  108.           final Path familydir)  
  109.       throws IOException {  
  110.         close(writer);  
  111.         return new HFile.Writer(fs,  StoreFile.getUniqueFile(fs, familydir),  
  112.           blocksize, compression, KeyValue.KEY_COMPARATOR);  
  113.       }  
  114.   
  115.       private void close(final HFile.Writer w) throws IOException {  
  116.         if (w != null) {  
  117.           StoreFile.appendMetadata(w, System.currentTimeMillis(), true);  
  118.           w.close();  
  119.         }  
  120.       }  
  121.   
  122.       public void close(TaskAttemptContext c)  
  123.       throws IOException, InterruptedException {  
  124.         for (Map.Entry<byte [], WriterLength> e: this.writers.entrySet()) {  
  125.           close(e.getValue().writer);  
  126.         }  
  127.       }  
  128.     };  
  129.   }  
  130.   
  131.   /* 
  132.    * Data structure to hold a Writer and amount of data written on it.  
  133.    */  
  134.   static class WriterLength {  
  135.     long written = 0;  
  136.     HFile.Writer writer = null;  
  137.   }  
  138. }  


可以看到,它的工作流程就是首先根据你的配置文件初始化,然后写成hfile的格式。 
这里我做了个偷懒的demo: 
Java代码  收藏代码
  1. HFileOutputFormat hf = new HFileOutputFormat();  
  2.         HBaseConfiguration conf = new HBaseConfiguration();  
  3.         conf.addResource(new Path("/home/performance/softs/hadoop/conf/core-site.xml"));  
  4.         conf.set("mapred.output.dir""/tmp");  
  5.         conf.set("hfile.compression", Compression.Algorithm.LZO.getName());  
  6.         TaskAttemptContext context = new TaskAttemptContext(conf, new TaskAttemptID());  
  7.         RecordWriter writer = hf.getRecordWriter(context);  
  8.         KeyValue kv = new KeyValue(Bytes.toBytes("1111111111111"), Bytes.toBytes("offer:action"),  
  9.                                    System.currentTimeMillis(), Bytes.toBytes("test"));  
  10.         KeyValue kv1 = new KeyValue(Bytes.toBytes("1111111111111"), Bytes.toBytes("offer:id"),  
  11.                                     System.currentTimeMillis(), Bytes.toBytes("123"));  
  12.         KeyValue kv3 = new KeyValue(Bytes.toBytes("1111111111112"), Bytes.toBytes("offer:action"),  
  13.                                     System.currentTimeMillis(), Bytes.toBytes("test"));  
  14.         KeyValue kv4 = new KeyValue(Bytes.toBytes("1111111111112"), Bytes.toBytes("offer:id"),  
  15.                                     System.currentTimeMillis(), Bytes.toBytes("123"));  
  16.         writer.write(null, kv);  
  17.         writer.write(null, kv1);  
  18.         writer.write(null, kv3);  
  19.         writer.write(null, kv4);  
  20.         writer.close(context);  

执行然之后,会在hdfs的/tmp目录下生成一份文件。注意批量写数据的时候一定要保证key的有序性 
这个时候,hbase自己提供的一个基于jruby的loadtable.rb脚本就可以发挥作用了。 
它的格式是loadtable.rb 你希望的表明 hdfs路径: 
hbase org.jruby.Main loadtable.rb offer hdfs://user/root/importoffer/_temporary/_attempt__0000_r_000000_0/ 
执行完之后: 
运行./hbase shell 
>list 
就会显示刚才导入的offer表了。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值