1. 程式人生 > >優化hbase的查詢優化-大幅提升讀寫速率(轉)

優化hbase的查詢優化-大幅提升讀寫速率(轉)

問題導讀:
1.本文通過什麼方法優化查詢效率的?
2.如何增大RPC數量?
3.如何調整hbase記憶體?






環境:suse  8G記憶體,8核,12T磁碟
          hbase master 佔一臺,其他7臺作為hbase的region
注意:此處不討論hadoop

情景
          我們有7億的資料,需要做查詢操作,需要從1.7億的表中查詢一個欄位,並寫入到7億資料的表中。
          這裡為了描述方便,將7億資料的表稱為:A表,1.7億資料的表稱為B表。

          在初期,我們採用的邏輯是:將A表中的資料讀取一行,根據其中的某個欄位去組織一個GET,然後
          立刻提交,從B表取得要查詢的欄位的值,組織成一個PUT,並提交到A表。

          那麼這麼做的話,完全發揮不出hbase的效率,因為每個get之間它的key不一定連續,或者說是在同一範圍
          而hbase的服務端會根據每個請求去載入資料到記憶體,由於請求的塊分散,那麼資料在記憶體中的替換過多的頻繁。
          很有可能和直接讀取磁碟資料差不多。
          並且採用這種邏輯去處理資料,需要花費時間太多。差不多是10W行讀寫資料需要40分鐘。這對於A表的更新操作
          完全是不能接受的。

          之後,通過讀資料的讀取操作進行封裝,組織成一個ArrayList<Get> 當到一定程度的時候採取提交。這裡還有一個情況就是

有些資料查詢不到,那麼需要去連線資料庫去申請一個自動分配的值,並立刻提交,因為後面可能有請求這個資料。
          這就需要分開處理。
   在組織GET 列表的時候需要先查詢,注意,不要採用table.get去取一個cell的值並判斷是否為null來處理。
          而是用table.exist(get) 去查詢,這是在server-side跑的,效率要高很多。
          對於查詢不到的值立刻申請值並寫入A表。
          對於查詢的到的,那麼就可前面說的組織get 加入到GET列表中,到一定程度再去一次提交,在取到值之後,
          在根據將迴圈資料的記錄,將這些組織成put,數量和GET列表一樣,不要去具體指定,在迴圈一次後直接table.put


          其他引數修改方面,寫的都很多,這裡就不提了。

處理速度(取至其中一臺伺服器中跑的任務):
2011-12-30 17:10:03 Start Write Lines:1700000
2011-12-30 17:14:10 Writed Lines:1700000
2011-12-30 17:14:11 Start Write Lines:1800000
2011-12-30 17:18:21 Writed Lines:1800000
2011-12-30 17:18:22 Start Write Lines:1900000
2011-12-30 17:22:29 Writed Lines:1900000
2011-12-30 17:22:29 Start Write Lines:2000000
2011-12-30 17:26:37 Writed Lines:2000000
2011-12-30 17:26:37 Start Write Lines:2100000

大約是查詢,寫入速度是4分鐘處理10W行資料。
也就是4000/s的速率,較之前的處理方式提升了一個量級






參考:
1、使用bloomfilter和mapfile_index_interval
   Bloomfilter(開啟/未開啟=1/0)    mapfile_index_interval Exists(0-10000)/ms Get(10001 - 20000)/ms
0 128 22460 23715
0 0 11897 11416
0 64 13692 14034
1 128 3275 3686
1 64 2961 3010
1 0 3339 3498
測試環境為:單機,規模為10萬條資料。隨機在10000條資料中有99條存在的情況下。
結論:開啟bloomfilter比沒開啟要快3、4倍。而適當的減少mapfile_index_interval可以提升效能 注意: 在1.9.3版本的hbase中,bloomfilter是不支援的,存在一個bug,可以通過如下的修改加以改正:
    (1)、在方法org.apache.hadoop.hbase.region.HStore.createReaders()中,找到如下行
    BloomFilterMapFile.Reader reader = file.getReader(fs, false, false);
    將其改成
    BloomFilterMapFile.Reader reader = file.getReader(fs, this.family.isBloomfilter(), false);
    (2)、在方法org.apache.hadoop.hbase.HColumnDescriptor.toString()中,找到如下的程式碼行
      if (key != null && key.toUpperCase().equals(BLOOMFILTER)) {
        // Don't emit bloomfilter.  Its not working.
        continue;
      }
    將其註釋掉

2、hbase對於記憶體有特別的嗜好,在硬體允許的情況下配足夠多的記憶體給它。
    通過修改hbase-env.sh中的
    export HBASE_HEAPSIZE=3000 #這裡預設為1000m

3、修改java虛擬機器屬性
    (1)、在環境允許的情況下換64位的虛擬機器
    (2)、替換掉預設的垃圾回收器,因為預設的垃圾回收器在多執行緒環境下會有更多的wait等待
    export HBASE_OPTS="-server -XX:NewSize=6m -XX:MaxNewSize=6m -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode"

4、增大RPC數量
    通過修改hbase-site.xml中的   
    hbase.region.handler.count屬性,可以適當的放大。預設值為10有點小

5、做程式開發是注意的地方
    (1)、需要判斷所求的資料行是否存在時,儘量不要用HTable.exists(final byte [] row) 而用HTable.exists(final byte [] row, final byte[] column)等帶列族的方法替代。
    (2)、不要使用HTable.get(final byte [] row, final byte [] column) == null來判斷所求的資料存在,而是用HTable.exists(final byte [] row, final byte[] column)替代
    (3)、HTable.close()方法少用.因為我遇到過一些很令人費解的錯誤

6、記住HBase是基於列模式的儲存,如果一個列族能搞定就不要把它分開成兩個,關係資料庫的那套在這裡很不實用.分成多個列來儲存會浪費更多的空間,除非你認為現在的硬碟和白菜一個價。

7、如果資料量沒有達到TB級別或者沒有上億條記錄,很難發揮HBase的優勢,建議換關係資料庫或別的儲存技術。