測試環境
測試硬體:4核i5處理器,8G記憶體,1T硬碟,千兆網路
測試軟體:ubuntu12.10 64位,hadoop版本:0.20.205,hbase版本:0.90.5
測試設定:一個master(namenode)和三台resigonServer(datanode),向HBase叢集寫入1千萬個資料(一個資料15K左右)
測試結果
- 第一列和最後一列分別是插入相同資料再HBase中和HDFS中,可以看見差距很大,HBase上資料的插入時間是HDFS的10倍左右
- 向HBase中插入資料比HDFS效能差這麼多,筆者就研究一下是什麼原因讓HBase寫效能這麼不好。向HBase中插入資料的過程大致是這樣:client插入資料時先向master請求,master回複哪個resigionserver的哪個region可以給插入資料,然後client直接和resigionserver通訊插入資料,resigionserver判斷該資料插入到哪個datablock裡(resigion是由datablock組成的),然後以HFile的形式儲存在HDFS中(資料不一定在resigionserver本地)。具體過程如下可以參考部落格http://jiajun.iteye.com/blog/899632
- 影響HBase寫入效能的一個因素就是用put類插入資料的緩衝區問題。用put類插入資料時,預設的情況是寫入一次資料由clinet和resigionserver進行一次RPC來插入資料。由於是1千萬個資料,多次進行處理序間通訊勢必會影響時間。HBase給用戶端提供了寫緩衝區,當緩衝區填滿之後才執行寫入操作,這樣就減少了寫入的測次數。
- 首先取消自動寫入,setAutoFlush(false)
- 然後設定寫緩衝區大小(預設是2MB)setWriteBufferSize()或者更改hbase-site.xml的hbase.client.write.buffer的屬性
- 上面列表可以看出把緩衝區設為20M還是對寫入時間有改進,但是改成200M寫入時間更長(為什嗎?)
- 另一個因素就是WAL(write ahead log),因為每一個resigion都有一個memstore用記憶體來暫時存放資料,進行排序,最後再吸入HFile裡面去,這樣做為了減少磁碟尋道而節省時間,但是為了災難恢複,所以會把記憶體中的資料進行記錄。所以筆者把WAL關閉之後,又測了下效能,還是有一點協助的,但是協助不是太大,可見WAL不是寫入的瓶頸。(setWriteToWal(false))
- 因為HBase對查詢方便,能夠快速的讀取資料,寫入時必然會採取一些措施進行排序,這就是HBase的合并和分裂機制。HBase官方為了提升寫入效能,給出一種方案就是預分配resigion,也就是池的概念,你先分配一些resigion,用的時候直接用就行了。本來這1千萬個資料要儲存900個resigion,所以筆者預先分配了150個resigion(分配900個resigion,建表時間太長,出現異常,還沒有解決),結果寫入時間提升了很多,基本是原來的一半,如果能預先分配900個resigion,應該更能節省時間。
ps: 寫入時,設定緩衝區越大,寫資料(比如8位元組)的時間越短
pps:寫入buffer不宜設成過大 M級就可以。最近寫入測試又做了一些實驗,除了預先分配region之外,用多線程寫效率還是很高的,筆者測試40個線程比單線程快了8倍左右
測試程式
package GIS.Update;import java.io.IOException;import java.math.BigInteger;import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.fs.FSDataOutputStream;import org.apache.hadoop.fs.FileSystem;import org.apache.hadoop.fs.Path;import org.apache.hadoop.hbase.HBaseConfiguration;import org.apache.hadoop.hbase.HColumnDescriptor;import org.apache.hadoop.hbase.HTableDescriptor;import org.apache.hadoop.hbase.MasterNotRunningException;import org.apache.hadoop.hbase.ZooKeeperConnectionException;import org.apache.hadoop.hbase.client.HBaseAdmin;import org.apache.hadoop.hbase.client.HTable;import org.apache.hadoop.hbase.client.Put;import org.apache.hadoop.hbase.util.Bytes;public class TestUpdate {public static void testHDFS() throws IOException{String str="hdfs://cloudgis4:9000/usr/tmp/";Path path=new Path(str);Configuration conf=new Configuration();conf.addResource(new Path("/usr/local/hadoop/conf/hdfs-site.xml"));FileSystem hdfs=path.getFileSystem(conf);hdfs.setReplication(path, (short)4);FSDataOutputStream fsDataOut=hdfs.create(new Path(str+"zzz"));long begin=System.currentTimeMillis();for(int i=0;i<10000000;i++){//byte [] kkk=new byte[10000+i/1000];byte [] kkk=new byte[12]; fsDataOut.write(kkk);//fsDataOut.close();//hdfs.close();}fsDataOut.close();long end=System.currentTimeMillis();System.out.println("hdfs:"+(end-begin));}public static void testHBase() throws IOException{Configuration conf=HBaseConfiguration.create();conf.addResource(new Path("/usr/local/hbase/conf/hbase-site.xml"));//conf.addResource("/usr/local/hbase/conf/hdfs-site.xml");HBaseAdmin admin=new HBaseAdmin(conf);String tableName="qq";String familyName="imageFamily";String columnName="imageColumn";HTableDescriptor htd=new HTableDescriptor (tableName);HColumnDescriptor hdc=new HColumnDescriptor(familyName);htd.addFamily(hdc);long before=System.currentTimeMillis();//admin.createTable(htd,splits);admin.createTable(htd,Bytes.toBytes("0000000"),Bytes.toBytes("9999999"),150);long after=System.currentTimeMillis();System.out.println(after-before);HTable table=new HTable(conf,htd.getName());table.setAutoFlush(false);//table.setWriteBufferSize(209715200); System.out.println(table.getWriteBufferSize());long begin=System.currentTimeMillis(); for(int i=0;i<10000000;i++){byte [] kkk=new byte[10000+i/1000]; //byte [] kkk=new byte[12]; Put p1=new Put(Bytes.toBytes(intToString(i)));p1.setWriteToWAL(false);p1.add(Bytes.toBytes(familyName),Bytes.toBytes(columnName),kkk); table.put(p1);}long end=System.currentTimeMillis();table.flushCommits();System.out.println("HBase:"+(end-begin));}static public String intToString(int x){String result=String.valueOf(x);int size=result.length();while(size<7){size++;result="0"+result;}return result;} public static void main(String []args) throws IOException{testHBase();}}