HBase(十)HBase性能調優總結


一. HBase的通用優化

1 高可用

     在 HBase 中 Hmaster 負責監控 RegionServer 的生命周期,均衡 RegionServer 的負載,如果 Hmaster 掛掉了,那么整個 HBase 集群將陷入不健康的狀態,並且此時的工作狀態並不會維持太久。所以 HBase 支持對 Hmaster 的高可用配置。

HBase的高可用集群搭建參考: CentOS7.5搭建HBase1.2.6HA集群

Hadoop 的通用性優化

1) NameNode 元數據備份使用 SSD

2) 定時備份 NameNode 上的元數據

每小時或者每天備份,如果數據極其重要,可以 5~10 分鍾備份一次。備份可以通過定時任務復制元數據目錄即可。

3) 為 NameNode 指定多個元數據目錄

使用 dfs.name.dir 或者 dfs.namenode.name.dir 指定。這樣可以提供元數據的冗余和健壯性, 以免發生故障。

4) NameNode 的 dir 自恢復

設置 dfs.namenode.name.dir.restore 為 true,允許嘗試恢復之前失敗的 dfs.namenode.name.dir

目錄,在創建 checkpoint 時做此嘗試,如果設置了多個磁盤,建議允許。

5) HDFS 保證 RPC 調用會有較多的線程數

屬性:dfs.namenode.handler.count
解釋:該屬性是 NameNode 服務默認線程數,默認值是 10,根據機器的可用內存可以調整為 50~100
屬性:dfs.datanode.handler.count
解釋:該屬性默認值為 10,是 DataNode 的處理線程數,如果 HDFS 客戶端程序讀寫請求比較多,可以調高到 15~20,設置的值越大,內存消耗越多,不要調整的過高,一般業務中,
5~10 即可。

hdfs-site.xml

6) HDFS 副本數的調整

屬性:dfs.replication
解釋:如果數據量巨大,且不是非常之重要,可以調整為 2~3,如果數據非常之重要,可以調整為 3~5。

hdfs-site.xml

7) HDFS 文件塊大小的調整

屬性:dfs.blocksize
解釋:塊大小定義,該屬性應該根據存儲的大量的單個文件大小來設置,如果大量的單個文件都小於 100M,
建議設置成 64M 塊大小,對於大於 100M 或者達到 GB 的這種情況,建議設置成 256M,一般設置范圍波動在 64M~256M 之間。

hdfs-site.xml

8) MapReduce Job 任務服務線程數調整

屬性:mapreduce.jobtracker.handler.count
解釋:該屬性是 Job 任務線程數,默認值是 10,根據機器的可用內存可以調整為 50~100

mapred-site.xml

9) Http 服務器工作線程數

mapred-site.xml

屬性:mapreduce.tasktracker.http.threads
解釋:定義 HTTP 服務器工作線程數,默認值為 40,對於大集群可以調整到 80~100

10) 文件排序合並優化

mapred-site.xml

屬性:mapreduce.task.io.sort.factor
解釋:文件排序時同時合並的數據流的數量,這也定義了同時打開文件的個數,默認值為
10,如果調高該參數,可以明顯減少磁盤 IO,即減少文件讀取的次數。

11) 設置任務並發

mapred-site.xml

屬性:mapreduce.map.speculative
解釋:該屬性可以設置任務是否可以並發執行,如果任務多而小,該屬性設置為 true 可以明顯加快任務執行效率,但是對於延遲非常高的任務,建議改為 false,這就類似於迅雷下載。

12) MR 輸出數據的壓縮

mapred-site.xml

屬性:mapreduce.map.output.compress、mapreduce.output.fileoutputformat.compress
解釋:對於大集群而言,建議設置 Map-Reduce 的輸出為壓縮的數據,而對於小集群,則不需要。

13) 優化 Mapper 和 Reducer 的個數

mapred-site.xml

屬性:mapreduce.tasktracker.map.tasks.maximum mapreduce.tasktracker.reduce.tasks.maximum
解釋:以上兩個屬性分別為一個單獨的 Job 任務可以同時運行的 Map 和 Reduce 的數量。
設置上面兩個參數時,需要考慮 CPU 核數、磁盤和內存容量。假設一個 8 核的 CPU,業務內容非常消耗 CPU,那么可以設置 map 數量為 4,如果該業務不是特別消耗 CPU 類型的,
那么可以設置 map 數量為 40,reduce 數量為 20。這些參數的值修改完成之后,一定要觀察是否有較長等待的任務,如果有的話,可以減少數量以加快任務執行,
如果設置一個很大的值,會引起大量的上下文切換,以及內存與磁盤之間的數據交換,這里沒有標准的配置數值, 需要根據業務和硬件配置以及經驗來做出選擇。 在同一時刻,不要同時運行太多的 MapReduce,這樣會消耗過多的內存,任務會執行的非常緩慢,我們需要根據 CPU 核數,內存容量設置一個 MR 任務並發的最大值,
使固定數據量的任務完全加載到內存中,避免頻繁的內存和磁盤數據交換,從而降低磁盤 IO,提高性能。

大概估算公式:

map = 2 + ⅔cpu_core,       reduce = 2 + ⅓cpu_core

Linux 優化

1) 開啟文件系統的預讀緩存可以提高讀取速度

$ sudo blockdev --setra 32768 /dev/sda

尖叫提示:ra 是 readahead 的縮寫

2) 關閉進程睡眠池

即不允許后台進程進入睡眠狀態,如果進程空閑,則直接 kill 掉釋放資源

$ sudo sysctl -w vm.swappiness=0

3) 調整 ulimit 上限,默認值為比較小的數字

$ ulimit -n 查看允許最大進程數
$ ulimit -u 查看允許打開最大文件數

優化修改:

末尾添加:

 

*

soft

nofile

1024000

*

hard

nofile

1024000

Hive

-

nofile

1024000

hive

-

nproc

1024000

4) 開啟集群的時間同步 NTP

集群中某台機器同步網絡時間服務器的時間,集群中其他機器則同步這台機器的時間。

5) 更新系統補丁

更新補丁前,請先測試新版本補丁對集群節點的兼容性。

Zookeeper 優化

1) 優化 Zookeeper 會話超時時間

hbase-site.xml

參數:zookeeper.session.timeout
解 釋 :In hbase-site.xml, set zookeeper.session.timeout to 30 seconds or less to bound failure detection (20-30 seconds is a good start).
該值會直接關系到 master 發現服務器宕機的最大周期,默認值為 30 秒,如果該值過小,會在 HBase 在寫入大量數據發生而 GC 時,導致RegionServer 短暫的不可用,
從而沒有向 ZK 發送心跳包,最終導致認為從節點 shutdown。一般 20 台左右的集群需要配置 5 台 zookeeper

二. HBase的個性優化

1 預分區及RowKey設計

詳細請看:HBase表以及Rowkey的設計原則

2 內存優化

HBase 操作過程中需要大量的內存開銷,畢竟 Table 是可以緩存在內存中的,一般會分配整個可用內存的 70%給 HBase 的 Java 堆。但是不建議分配非常大的堆內存,因為 GC 過程持續太久會導致 RegionServer 處於長期不可用狀態,一般 16~48G 內存就可以了,如果因為框架占用內存過高導致系統內存不足,框架一樣會被系統服務拖死。

3  基礎優化

1) 允許在 HDFS 的文件中追加內容

不是不允許追加內容么?沒錯,請看背景故事:http://blog.cloudera.com/blog/2009/07/file-appends-in-hdfs/

hdfs-site.xmlhbase-site.xml

屬性:dfs.support.append
解釋:開啟 HDFS 追加同步,可以優秀的配合 HBase 的數據同步和持久化。默認值為 true.

2) 優化 DataNode 允許的最大文件打開數

hdfs-site.xml

屬性:dfs.datanode.max.transfer.threads
解釋:HBase 一般都會同一時間操作大量的文件,根據集群的數量和規模以及數據動作,設置為 4096 或者更高。默認值:4096

3) 優化延遲高的數據操作的等待時間

hdfs-site.xml

屬性:dfs.image.transfer.timeout
解釋:如果對於某一次數據操作來講,延遲非常高,socket 需要等待更長的時間,建議把該值設置為更大的值(默認 60000 毫秒),以確保 socket 不會被 timeout 掉。

4) 優化數據的寫入效率

mapred-site.xml

屬性:
mapreduce.map.output.compress    mapreduce.map.output.compress.codec
解釋:開啟這兩個數據可以大大提高文件的寫入效率,減少寫入時間。第一個屬性值修改為true,第二個屬性值修改為:org.apache.hadoop.io.compress.GzipCodec 或者其他壓縮方式

5) 優化 DataNode 存儲

屬性:dfs.datanode.failed.volumes.tolerated
解釋:默認為 0,意思是當 DataNode 中有一個磁盤出現故障,則會認為該 DataNode shutdown 了。
如果修改為 1,則一個磁盤出現故障時,數據會被復制到其他正常的 DataNode 上,當前的 DataNode 繼續工作。

6) 設置 RPC 監聽數量

hbase-site.xml

屬性:hbase.regionserver.handler.count
解釋:默認值為 30,用於指定 RPC 監聽的數量,可以根據客戶端的請求數進行調整,讀寫請求較多時,增加此值

7) 優化 HStore 文件大小

hbase-site.xml

屬性:hbase.hregion.max.filesize
解釋:默認值 10737418240(10GB),如果需要運行 HBase 的 MR 任務,可以減小此值, 因為一個 region 對應一個 map 任務,
如果單個 region 過大,會導致 map 任務執行時間過長。該值的意思就是,如果 HFile 的大小達到這個數值,則這個 region 會被切分為兩個 Hfile。

8) 優化 hbase 客戶端緩存

hbase-site.xml

屬性:hbase.client.write.buffer
解釋:用於指定 HBase 客戶端緩存,增大該值可以減少 RPC 調用次數,但是會消耗更多內存,反之則反之。一般我們需要設定一定的緩存大小,以達到減少 RPC 次數的目的。

9) 指定 scan.next 掃描 HBase 所獲取的行數

hbase-site.xml

屬性:hbase.client.scanner.caching
解釋:用於指定 scan.next 方法獲取的默認行數,值越大,消耗內存越大。

10) flushcompactsplit 機制

當 MemStore 達到閾值,將 Memstore 中的數據 Flush 進 Storefile;compact 機制則是把 flush 出來的小文件合並成大的 Storefile 文件。split 則是當 Region 達到閾值,會把過大的 Region 一分為二。

涉及屬性:

即:128M 就是 Memstore 的默認閾值

hbase.hregion.memstore.flush.size:134217728

即:這個參數的作用是當單個 HRegion 內所有的 Memstore 大小總和超過指定值時,flush

該 HRegion 的所有 memstore。RegionServer 的 flush 是通過將請求添加一個隊列,模擬生產消費模型來異步處理的。那這里就有一個問題,當隊列來不及消費,產生大量積壓請求時,可能會導致內存陡增,最壞的情況是觸發 OOM。

hbase.regionserver.global.memstore.upperLimit:0.4 
hbase.regionserver.global.memstore.lowerLimit:0.38

即:當 MemStore 使用內存總量達到 hbase.regionserver.global.memstore.upperLimit 指定值時,將會有多個 MemStores flush 到文件中,MemStore flush 順序是按照大小降序執行的,直到刷新到 MemStore 使用內存略小於 lowerLimit

三. HBase的寫表優化

1 多HTable並發寫

創建多個HTable客戶端用於寫操作,提高寫數據的吞吐量,一個例子:

static final Configuration conf = HBaseConfiguration.create();
static final String table_log_name = “user_log”;
wTableLog = new HTable[tableN];
for (int i = 0; i < tableN; i++) {
    wTableLog[i] = new HTable(conf, table_log_name);
    wTableLog[i].setWriteBufferSize(5 * 1024 * 1024); //5MB
    wTableLog[i].setAutoFlush(false);
}

2  HTable參數設置

Auto Flush

通過調用HTable.setAutoFlush(false)方法可以將HTable寫客戶端的自動flush關閉,這樣可以批量寫入數據到HBase,而不是有一條put就執行一次更新,只有當put填滿客戶端寫緩存時,才實際向HBase服務端發起寫請求。默認情況下auto flush是開啟的。

Write Buffer

通過調用HTable.setWriteBufferSize(writeBufferSize)方法可以設置HTable客戶端的寫buffer大小,如果新設置的buffer小於當前寫buffer中的數據時,buffer將會被flush到服務端。其中,writeBufferSize的單位是byte字節數,可以根據實際寫入數據量的多少來設置該值。

WAL Flag

在HBae中,客戶端向集群中的RegionServer提交數據時(Put/Delete操作),首先會先寫WAL(Write Ahead Log)日志(即HLog,一個RegionServer上的所有Region共享一個HLog),只有當WAL日志寫成功后,再接着寫MemStore,然后客戶端被通知提交數據成功;如果寫WAL日志失敗,客戶端則被通知提交失敗。這樣做的好處是可以做到RegionServer宕機后的數據恢復。

因此,對於相對不太重要的數據,可以在Put/Delete操作時,通過調用Put.setWriteToWAL(false)或Delete.setWriteToWAL(false)函數,放棄寫WAL日志,從而提高數據寫入的性能。

值得注意的是:謹慎選擇關閉WAL日志,因為這樣的話,一旦RegionServer宕機,Put/Delete的數據將會無法根據WAL日志進行恢復。

3 批量寫

通過調用HTable.put(Put)方法可以將一個指定的row key記錄寫入HBase,同樣HBase提供了另一個方法:通過調用HTable.put(List<Put>)方法可以將指定的row key列表,批量寫入多行記錄,這樣做的好處是批量執行,只需要一次網絡I/O開銷,這對於對數據實時性要求高,網絡傳輸RTT高的情景下可能帶來明顯的性能提升。

多線程並發寫

在客戶端開啟多個HTable寫線程,每個寫線程負責一個HTable對象的flush操作,這樣結合定時flush和寫buffer(writeBufferSize),可以既保證在數據量小的時候,數據可以在較短時間內被flush(如1秒內),同時又保證在數據量大的時候,寫buffer一滿就及時進行flush。下面給個具體的例子:

for (int i = 0; i < threadN; i++) {
    Thread th = new Thread() {
        public void run() {
            while (true) {
                try {
                    sleep(1000); //1 second
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
synchronized (wTableLog[i]) {
                    try {
                        wTableLog[i].flushCommits();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
}
    };
    th.setDaemon(true);
    th.start();
}

四. HBase的讀表優化

1 多HTable並發讀

創建多個HTable客戶端用於讀操作,提高讀數據的吞吐量,一個例子:

static final Configuration conf = HBaseConfiguration.create();
static final String table_log_name = “user_log”;
rTableLog
= new HTable[tableN]; for (int i = 0; i < tableN; i++) { rTableLog[i] = new HTable(conf, table_log_name); rTableLog[i].setScannerCaching(50); }

2  HTable參數設置

Scanner Caching

hbase.client.scanner.caching配置項可以設置HBase scanner一次從服務端抓取的數據條數,默認情況下一次一條。通過將其設置成一個合理的值,可以減少scan過程中next()的時間開銷,代價是scanner需要通過客戶端的內存來維持這些被cache的行記錄。

有三個地方可以進行配置:三者的優先級越來越高。

1)在HBase的conf配置文件中進行配置;

2)通過調用HTable.setScannerCaching(int scannerCaching)進行配置;

3)通過調用Scan.setCaching(int caching)進行配置。

Scan Attribute Selection

scan時指定需要的Column Family,可以減少網絡傳輸數據量,否則默認scan操作會返回整行所有Column Family的數據。

Close ResultScanner

通過scan取完數據后,記得要關閉ResultScanner,否則RegionServer可能會出現問題(對應的Server資源無法釋放)。

3 批量讀

通過調用HTable.get(Get)方法可以根據一個指定的row key獲取一行記錄,同樣HBase提供了另一個方法:通過調用HTable.get(List<Get>)方法可以根據一個指定的row key列表,批量獲取多行記錄,這樣做的好處是批量執行,只需要一次網絡I/O開銷,這對於對數據實時性要求高而且網絡傳輸RTT高的情景下可能帶來明顯的性能提升。

4 多線程並發讀

在客戶端開啟多個HTable讀線程,每個讀線程負責通過HTable對象進行get操作。下面是一個多線程並發讀取HBase,獲取店鋪一天內各分鍾PV值的例子:

public class DataReaderServer {
     //獲取店鋪一天內各分鍾PV值的入口函數
     public static ConcurrentHashMap<String, String> getUnitMinutePV(long uid, long startStamp, long endStamp){
         long min = startStamp;
         int count = (int)((endStamp - startStamp) / (60*1000));
         List<String> lst = new ArrayList<String>();
         for (int i = 0; i <= count; i++) {
            min = startStamp + i * 60 * 1000;
            lst.add(uid + "_" + min);
         }
         return parallelBatchMinutePV(lst);
     }
      //多線程並發查詢,獲取分鍾PV值
private static ConcurrentHashMap<String, String> parallelBatchMinutePV(List<String> lstKeys){
        ConcurrentHashMap<String, String> hashRet = new ConcurrentHashMap<String, String>();
        int parallel = 3;
        List<List<String>> lstBatchKeys  = null;
        if (lstKeys.size() < parallel ){
            lstBatchKeys  = new ArrayList<List<String>>(1);
            lstBatchKeys.add(lstKeys);
        }
        else{
            lstBatchKeys  = new ArrayList<List<String>>(parallel);
            for(int i = 0; i < parallel; i++  ){
                List<String> lst = new ArrayList<String>();
                lstBatchKeys.add(lst);
            }

            for(int i = 0 ; i < lstKeys.size() ; i ++ ){
                lstBatchKeys.get(i%parallel).add(lstKeys.get(i));
            }
        }
        
        List<Future< ConcurrentHashMap<String, String> >> futures = new ArrayList<Future< ConcurrentHashMap<String, String> >>(5);
        
        ThreadFactoryBuilder builder = new ThreadFactoryBuilder();
        builder.setNameFormat("ParallelBatchQuery");
        ThreadFactory factory = builder.build();
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(lstBatchKeys.size(), factory);
        
        for(List<String> keys : lstBatchKeys){
            Callable< ConcurrentHashMap<String, String> > callable = new BatchMinutePVCallable(keys);
            FutureTask< ConcurrentHashMap<String, String> > future = (FutureTask< ConcurrentHashMap<String, String> >) executor.submit(callable);
            futures.add(future);
        }
        executor.shutdown();
        
        // Wait for all the tasks to finish
        try {
          boolean stillRunning = !executor.awaitTermination(
              5000000, TimeUnit.MILLISECONDS);
          if (stillRunning) {
            try {
                executor.shutdownNow();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
          }
        } catch (InterruptedException e) {
          try {
              Thread.currentThread().interrupt();
          } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
          }
        }
        
        // Look for any exception
        for (Future f : futures) {
          try {
              if(f.get() != null)
              {
                  hashRet.putAll((ConcurrentHashMap<String, String>)f.get());
              }
          } catch (InterruptedException e) {
            try {
                 Thread.currentThread().interrupt();
            } catch (Exception e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
          } catch (ExecutionException e) {
            e.printStackTrace();
          }
        }
        
        return hashRet;
    }
     //一個線程批量查詢,獲取分鍾PV值
    protected static ConcurrentHashMap<String, String> getBatchMinutePV(List<String> lstKeys){
        ConcurrentHashMap<String, String> hashRet = null;
        List<Get> lstGet = new ArrayList<Get>();
        String[] splitValue = null;
        for (String s : lstKeys) {
            splitValue = s.split("_");
            long uid = Long.parseLong(splitValue[0]);
            long min = Long.parseLong(splitValue[1]);
            byte[] key = new byte[16];
            Bytes.putLong(key, 0, uid);
            Bytes.putLong(key, 8, min);
            Get g = new Get(key);
            g.addFamily(fp);
            lstGet.add(g);
        }
        Result[] res = null;
        try {
            res = tableMinutePV[rand.nextInt(tableN)].get(lstGet);
        } catch (IOException e1) {
            logger.error("tableMinutePV exception, e=" + e1.getStackTrace());
        }

        if (res != null && res.length > 0) {
            hashRet = new ConcurrentHashMap<String, String>(res.length);
            for (Result re : res) {
                if (re != null && !re.isEmpty()) {
                    try {
                        byte[] key = re.getRow();
                        byte[] value = re.getValue(fp, cp);
                        if (key != null && value != null) {
                            hashRet.put(String.valueOf(Bytes.toLong(key,
                                    Bytes.SIZEOF_LONG)), String.valueOf(Bytes
                                    .toLong(value)));
                        }
                    } catch (Exception e2) {
                        logger.error(e2.getStackTrace());
                    }
                }
            }
        }

        return hashRet;
    }
}
//調用接口類,實現Callable接口
class BatchMinutePVCallable implements Callable<ConcurrentHashMap<String, String>>{
     private List<String> keys;

     public BatchMinutePVCallable(List<String> lstKeys ) {
         this.keys = lstKeys;
     }

     public ConcurrentHashMap<String, String> call() throws Exception {
         return DataReadServer.getBatchMinutePV(keys);
     }
}

5 緩存查詢結果

對於頻繁查詢HBase的應用場景,可以考慮在應用程序中做緩存,當有新的查詢請求時,首先在緩存中查找,如果存在則直接返回,不再查詢HBase;否則對HBase發起讀請求查詢,然后在應用程序中將查詢結果緩存起來。至於緩存的替換策略,可以考慮LRU等常用的策略。

Blockcache

HBase上Regionserver的內存分為兩個部分,一部分作為Memstore,主要用來寫;另外一部分作為BlockCache,主要用於讀。

寫請求會先寫入Memstore,Regionserver會給每個region提供一個Memstore,當Memstore滿64MB以后,會啟動 flush刷新到磁盤。當Memstore的總大小超過限制時(heapsize * hbase.regionserver.global.memstore.upperLimit * 0.9),會強行啟動flush進程,從最大的Memstore開始flush直到低於限制。

讀請求先到Memstore中查數據,查不到就到BlockCache中查,再查不到就會到磁盤上讀,並把讀的結果放入BlockCache。由於BlockCache采用的是LRU策略,因此BlockCache達到上限(heapsize * hfile.block.cache.size * 0.85)后,會啟動淘汰機制,淘汰掉最老的一批數據。

一個Regionserver上有一個BlockCache和N個Memstore,它們的大小之和不能大於等於heapsize * 0.8,否則HBase不能啟動。默認BlockCache為0.2,而Memstore為0.4。對於注重讀響應時間的系統,可以將 BlockCache設大些,比如設置BlockCache=0.4,Memstore=0.39,以加大緩存的命中率。

有關BlockCache機制,請參考這里:HBase的Block cacheHBase的blockcache機制hbase中的緩存的計算與使用

五. HTable與HTable Pool

HTable和HTablePool使用注意事項

HTable和HTablePool都是HBase客戶端API的一部分,可以使用它們對HBase表進行CRUD操作。下面結合在項目中的應用情況,對二者使用過程中的注意事項做一下概括總結。

Configuration conf = HBaseConfiguration.create();
try (Connection connection = ConnectionFactory.createConnection(conf)) {
  try (Table table = connection.getTable(TableName.valueOf(tablename)) {
    // use table as needed, the table returned is lightweight
  }
}

1. HTable

HTable是HBase客戶端與HBase服務端通訊的Java API對象,客戶端可以通過HTable對象與服務端進行CRUD操作(增刪改查)。它的創建很簡單:

Configuration conf = HBaseConfiguration.create();
HTable table = new HTable(conf, "tablename");
//TODO CRUD Operation……

HTable使用時的一些注意事項:

1.   規避HTable對象的創建開銷

因為客戶端創建HTable對象后,需要進行一系列的操作:檢查.META.表確認指定名稱的HBase表是否存在,表是否有效等等,整個時間開銷比較重,可能會耗時幾秒鍾之長,因此最好在程序啟動時一次性創建完成需要的HTable對象,如果使用Java API,一般來說是在構造函數中進行創建,程序啟動后直接重用。

2.   HTable對象不是線程安全的

HTable對象對於客戶端讀寫數據來說不是線程安全的,因此多線程時,要為每個線程單獨創建復用一個HTable對象,不同對象間不要共享HTable對象使用,特別是在客戶端auto flash被置為false時,由於存在本地write buffer,可能導致數據不一致。

3.   HTable對象之間共享Configuration

HTable對象共享Configuration對象,這樣的好處在於:

  • 共享ZooKeeper的連接:每個客戶端需要與ZooKeeper建立連接,查詢用戶的table regions位置,這些信息可以在連接建立后緩存起來共享使用;
  • 共享公共的資源:客戶端需要通過ZooKeeper查找-ROOT-和.META.表,這個需要網絡傳輸開銷,客戶端緩存這些公共資源后能夠減少后續的網絡傳輸開銷,加快查找過程速度。

因此,與以下這種方式相比:

HTable table1 = new HTable("table1");

HTable table2 = new HTable("table2");

下面的方式更有效些:

Configuration conf = HBaseConfiguration.create();
HTable table1 = new HTable(conf, "table1");
HTable table2 = new HTable(conf, "table2");

備注:即使是高負載的多線程程序,也並沒有發現因為共享Configuration而導致的性能問題;如果你的實際情況中不是如此,那么可以嘗試不共享Configuration。

2.  HTable  Pool

HTablePool可以解決HTable存在的線程不安全問題,同時通過維護固定數量的HTable對象,能夠在程序運行期間復用這些HTable資源對象。

Configuration conf = HBaseConfiguration.create();
HTablePool pool = new HTablePool(conf, 10);

1.   HTablePool可以自動創建HTable對象,而且對客戶端來說使用上是完全透明的,可以避免多線程間數據並發修改問題。

2.   HTablePool中的HTable對象之間是公用Configuration連接的,能夠可以減少網絡開銷。

HTablePool的使用很簡單:每次進行操作前,通過HTablePool的getTable方法取得一個HTable對象,然后進行put/get/scan/delete等操作,最后通過HTablePool的putTable方法將HTable對象放回到HTablePool中。

下面是個使用HTablePool的簡單例子:

public void createUser(String username, String firstName, String lastName, String email, String password, String roles) throws IOException {

  HTable table = rm.getTable(UserTable.NAME);
  Put put = new Put(Bytes.toBytes(username));
  put.add(UserTable.DATA_FAMILY, UserTable.FIRSTNAME,
  Bytes.toBytes(firstName));
  put.add(UserTable.DATA_FAMILY, UserTable.LASTNAME,Bytes.toBytes(lastName));
  put.add(UserTable.DATA_FAMILY, UserTable.EMAIL, Bytes.toBytes(email));
  put.add(UserTable.DATA_FAMILY, UserTable.CREDENTIALS,Bytes.toBytes(password));
  put.add(UserTable.DATA_FAMILY, UserTable.ROLES, Bytes.toBytes(roles));
  table.put(put);
  table.flushCommits();
  rm.putTable(table);
}

HBase和DBMS比較:

查詢數據不靈活:

1、 不能使用column之間過濾查詢

2、 不支持全文索引。使用solr和hbase整合完成全文搜索。

a) 使用MR批量讀取hbase中的數據,在solr里面建立索引(no  store)之保存rowkey的值。

b) 根據關鍵詞從索引中搜索到rowkey(分頁)

c) 根據rowkey從hbase查詢所有數據


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM