在 HBase 中 Hmaster 負責監控 RegionServer 的生命週期,均衡 RegionServer 的負載,若是 Hmaster 掛掉了,那麼整個 HBase 集羣將陷入不健康的狀態,而且此時的工做狀態並不會維持過久。因此 HBase 支持對 Hmaster 的高可用配置。html
HBase的高可用集羣搭建參考: CentOS7.5搭建HBase1.2.6HA集羣node
1) NameNode 元數據備份使用 SSDapache
2) 定時備份 NameNode 上的元數據緩存
每小時或者天天備份,若是數據極其重要,能夠 5~10 分鐘備份一次。備份能夠經過定時任務複製元數據目錄便可。安全
3) 爲 NameNode 指定多個元數據目錄服務器
使用 dfs.name.dir 或者 dfs.namenode.name.dir 指定。這樣能夠提供元數據的冗餘和健壯性, 以避免發生故障。網絡
4) NameNode 的 dir 自恢復session
設置 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 便可。
6) HDFS 副本數的調整
屬性:dfs.replication 解釋:若是數據量巨大,且不是很是之重要,能夠調整爲 2~3,若是數據很是之重要,能夠調整爲 3~5。
7) HDFS 文件塊大小的調整
屬性:dfs.blocksize 解釋:塊大小定義,該屬性應該根據存儲的大量的單個文件大小來設置,若是大量的單個文件都小於 100M,
建議設置成 64M 塊大小,對於大於 100M 或者達到 GB 的這種狀況,建議設置成 256M,通常設置範圍波動在 64M~256M 之間。
8) MapReduce Job 任務服務線程數調整
屬性:mapreduce.jobtracker.handler.count 解釋:該屬性是 Job 任務線程數,默認值是 10,根據機器的可用內存能夠調整爲 50~100
9) Http 服務器工做線程數
屬性:mapreduce.tasktracker.http.threads 解釋:定義 HTTP 服務器工做線程數,默認值爲 40,對於大集羣能夠調整到 80~100
10) 文件排序合併優化
屬性:mapreduce.task.io.sort.factor 解釋:文件排序時同時合併的數據流的數量,這也定義了同時打開文件的個數,默認值爲 10,若是調高該參數,能夠明顯減小磁盤 IO,即減小文件讀取的次數。
11) 設置任務併發
屬性:mapreduce.map.speculative 解釋:該屬性能夠設置任務是否能夠併發執行,若是任務多而小,該屬性設置爲 true 能夠明顯加快任務執行效率,可是對於延遲很是高的任務,建議改成 false,這就相似於迅雷下載。
12) MR 輸出數據的壓縮
屬性:mapreduce.map.output.compress、mapreduce.output.fileoutputformat.compress 解釋:對於大集羣而言,建議設置 Map-Reduce 的輸出爲壓縮的數據,而對於小集羣,則不須要。
13) 優化 Mapper 和 Reducer 的個數
屬性: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
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) 更新系統補丁
更新補丁前,請先測試新版本補丁對集羣節點的兼容性。
1) 優化 Zookeeper 會話超時時間
參數: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表以及Rowkey的設計原則
HBase 操做過程當中須要大量的內存開銷,畢竟 Table 是能夠緩存在內存中的,通常會分配整個可用內存的 70%給 HBase 的 Java 堆。可是不建議分配很是大的堆內存,由於 GC 過程持續過久會致使 RegionServer 處於長期不可用狀態,通常 16~48G 內存就能夠了,若是由於框架佔用內存太高致使系統內存不足,框架同樣會被系統服務拖死。
1) 容許在 HDFS 的文件中追加內容
不是不容許追加內容麼?沒錯,請看背景故事:http://blog.cloudera.com/blog/2009/07/file-appends-in-hdfs/
屬性:dfs.support.append 解釋:開啓 HDFS 追加同步,能夠優秀的配合 HBase 的數據同步和持久化。默認值爲 true.
2) 優化 DataNode 容許的最大文件打開數
屬性:dfs.datanode.max.transfer.threads 解釋:HBase 通常都會同一時間操做大量的文件,根據集羣的數量和規模以及數據動做,設置爲 4096 或者更高。默認值:4096
3) 優化延遲高的數據操做的等待時間
屬性:dfs.image.transfer.timeout 解釋:若是對於某一次數據操做來說,延遲很是高,socket 須要等待更長的時間,建議把該值設置爲更大的值(默認 60000 毫秒),以確保 socket 不會被 timeout 掉。
4) 優化數據的寫入效率
屬性: 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.regionserver.handler.count
解釋:默認值爲 30,用於指定 RPC 監聽的數量,能夠根據客戶端的請求數進行調整,讀寫請求較多時,增長此值。
7) 優化 HStore 文件大小
屬性:hbase.hregion.max.filesize 解釋:默認值 10737418240(10GB),若是須要運行 HBase 的 MR 任務,能夠減少此值, 由於一個 region 對應一個 map 任務,
若是單個 region 過大,會致使 map 任務執行時間過長。該值的意思就是,若是 HFile 的大小達到這個數值,則這個 region 會被切分爲兩個 Hfile。
8) 優化 hbase 客戶端緩存
屬性:hbase.client.write.buffer 解釋:用於指定 HBase 客戶端緩存,增大該值能夠減小 RPC 調用次數,可是會消耗更多內存,反之則反之。通常咱們須要設定必定的緩存大小,以達到減小 RPC 次數的目的。
9) 指定 scan.next 掃描 HBase 所獲取的行數
屬性:hbase.client.scanner.caching 解釋:用於指定 scan.next 方法獲取的默認行數,值越大,消耗內存越大。
10) flush、compact、split 機制
當 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
建立多個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); }
經過調用HTable.setAutoFlush(false)方法能夠將HTable寫客戶端的自動flush關閉,這樣能夠批量寫入數據到HBase,而不是有一條put就執行一次更新,只有當put填滿客戶端寫緩存時,才實際向HBase服務端發起寫請求。默認狀況下auto flush是開啓的。
經過調用HTable.setWriteBufferSize(writeBufferSize)方法能夠設置HTable客戶端的寫buffer大小,若是新設置的buffer小於當前寫buffer中的數據時,buffer將會被flush到服務端。其中,writeBufferSize的單位是byte字節數,能夠根據實際寫入數據量的多少來設置該值。
在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日誌進行恢復。
經過調用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(); }
建立多個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); }
hbase.client.scanner.caching配置項能夠設置HBase scanner一次從服務端抓取的數據條數,默認狀況下一次一條。經過將其設置成一個合理的值,能夠減小scan過程當中next()的時間開銷,代價是scanner須要經過客戶端的內存來維持這些被cache的行記錄。
有三個地方能夠進行配置:三者的優先級愈來愈高。
1)在HBase的conf配置文件中進行配置;
2)經過調用HTable.setScannerCaching(int scannerCaching)進行配置;
3)經過調用Scan.setCaching(int caching)進行配置。
scan時指定須要的Column Family,能夠減小網絡傳輸數據量,不然默認scan操做會返回整行全部Column Family的數據。
經過scan取完數據後,記得要關閉ResultScanner,不然RegionServer可能會出現問題(對應的Server資源沒法釋放)。
經過調用HTable.get(Get)方法能夠根據一個指定的row key獲取一行記錄,一樣HBase提供了另外一個方法:經過調用HTable.get(List<Get>)方法能夠根據一個指定的row key列表,批量獲取多行記錄,這樣作的好處是批量執行,只須要一次網絡I/O開銷,這對於對數據實時性要求高並且網絡傳輸RTT高的情景下可能帶來明顯的性能提高。
在客戶端開啓多個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); } }
對於頻繁查詢HBase的應用場景,能夠考慮在應用程序中作緩存,當有新的查詢請求時,首先在緩存中查找,若是存在則直接返回,再也不查詢HBase;不然對HBase發起讀請求查詢,而後在應用程序中將查詢結果緩存起來。至於緩存的替換策略,能夠考慮LRU等經常使用的策略。
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 cache,HBase的blockcache機制,hbase中的緩存的計算與使用。
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 } }
HTable是HBase客戶端與HBase服務端通信的Java API對象,客戶端能夠經過HTable對象與服務端進行CRUD操做(增刪改查)。它的建立很簡單:
Configuration conf = HBaseConfiguration.create(); HTable table = new HTable(conf, "tablename"); //TODO CRUD Operation……
由於客戶端建立HTable對象後,須要進行一系列的操做:檢查.META.表確認指定名稱的HBase表是否存在,表是否有效等等,整個時間開銷比較重,可能會耗時幾秒鐘之長,所以最好在程序啓動時一次性建立完成須要的HTable對象,若是使用Java API,通常來講是在構造函數中進行建立,程序啓動後直接重用。
HTable對象對於客戶端讀寫數據來講不是線程安全的,所以多線程時,要爲每一個線程單首創建複用一個HTable對象,不一樣對象間不要共享HTable對象使用,特別是在客戶端auto flash被置爲false時,因爲存在本地write buffer,可能致使數據不一致。
HTable對象共享Configuration對象,這樣的好處在於:
所以,與如下這種方式相比:
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。
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比較:
查詢數據不靈活:
一、 不能使用column之間過濾查詢
二、 不支持全文索引。使用solr和hbase整合完成全文搜索。
a) 使用MR批量讀取hbase中的數據,在solr裏面創建索引(no store)之保存rowkey的值。
b) 根據關鍵詞從索引中搜索到rowkey(分頁)
c) 根據rowkey從hbase查詢全部數據