[TOC]
# HBASE內部原理
## 1 系統架構

### Client
1. 包含訪問hbase的接口,client維護著一些cache來加快對hbase的訪問,比如regione的位置信息。
### Zookeeper
1) 保證任何時候,集群中只有一個master
2) 存貯所有Region的尋址入口----root表在哪臺服務器上。
3) 實時監控Region Server的狀態,將Region server的上線和下線信息實時通知給Master
4) 存儲Hbase的schema,包括有哪些table,每個table有哪些column family
### Master職責
1) 為Region server分配region
2) 負責region server的負載均衡
3) 發現失效的region server并重新分配其上的region
4) HDFS上的垃圾文件回收
5) 處理schema更新請求
### Region Server職責
1) Region server維護Master分配給它的region,處理對這些region的IO請求
2) Region server負責切分在運行過程中變得過大的region
> 可以看到,client訪問hbase上數據的過程并不需要master參與(尋址訪問zookeeper和region server,數據讀寫訪問regione server),master僅僅維護者table和region的元數據信息,負載很低。
## 2 物理存儲
### 1、整體結構

1) Table中的所有行都按照row key的字典序排列。
2) Table 在行的方向上分割為多個Hregion。
3) region按大小分割的(默認10G),每個表一開始只有一個region,隨著數據不斷插入表,region不斷增大,當增大到一個閥值的時候,Hregion就會等分會兩個新的Hregion。當table中的行不斷增多,就會有越來越多的Hregion。
4) Hregion是Hbase中分布式存儲和負載均衡的最小單元。最小單元就表示不同的Hregion可以分布在不同的HRegion server上。但一個Hregion是不會拆分到多個server上的。
5) HRegion雖然是負載均衡的最小單元,但并不是物理存儲的最小單元。
> 事實上,HRegion由一個或者多個Store組成,每個store保存一個column family。
> 每個Strore又由一個memStore和0至多個StoreFile組成。如上圖

### 2、STORE FILE & HFILE結構
> StoreFile以HFile格式保存在HDFS上。
> 附:HFile的格式為:

> 首先HFile文件是不定長的,長度固定的只有其中的兩塊:Trailer和FileInfo。正如圖中所示的,Trailer中有指針指向其他數 據塊的起始點。
> File Info中記錄了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。
> Data Index和Meta Index塊記錄了每個Data塊和Meta塊的起始點。
> Data Block是HBase I/O的基本單元,為了提高效率,HRegionServer中有基于LRU的Block Cache機制。每個Data塊的大小可以在創建一個Table的時候通過參數指定,大號的Block有利于順序Scan,小號Block利于隨機查詢。 每個Data塊除了開頭的Magic以外就是一個個KeyValue對拼接而成, Magic內容就是一些隨機數字,目的是防止數據損壞。
> HFile里面的每個KeyValue對就是一個簡單的byte數組。但是這個byte數組里面包含了很多項,并且有固定的結構。我們來看看里面的具體結構:

> 開始是兩個固定長度的數值,分別表示Key的長度和Value的長度。緊接著是Key,開始是固定長度的數值,表示RowKey的長度,緊接著是 RowKey,然后是固定長度的數值,表示Family的長度,然后是Family,接著是Qualifier,然后是兩個固定長度的數值,表示Time Stamp和Key Type(Put/Delete)。Value部分沒有這么復雜的結構,就是純粹的二進制數據了。
> HFile分為六個部分:
1. Data Block 段–保存表中的數據,這部分可以被壓縮
2. Meta Block 段 (可選的)–保存用戶自定義的kv對,可以被壓縮。
3. File Info 段–Hfile的元信息,不被壓縮,用戶也可以在這一部分添加自己的元信息。
4. Data Block Index 段–Data Block的索引。每條索引的key是被索引的block的第一條記錄的key。
5. Meta Block Index段 (可選的)–Meta Block的索引。
6. Trailer–這一段是定長的。保存了每一段的偏移量,讀取一個HFile時,會首先 讀取Trailer,Trailer保存了每個段的起始位置(段的Magic Number用來做安全check),然后,DataBlock Index會被讀取到內存中,這樣,當檢索某個key時,不需要掃描整個HFile,而只需從內存中找到key所在的block,通過一次磁盤io將整個 block讀取到內存中,再找到需要的key。DataBlock Index采用LRU機制淘汰。
> HFile的Data Block,Meta Block通常采用壓縮方式存儲,壓縮之后可以大大減少網絡IO和磁盤IO,隨之而來的開銷當然是需要花費cpu進行壓縮和解壓縮。
> 目標Hfile的壓縮支持兩種方式:Gzip,Lzo。
### 3 Memstore與storefile
> 一個region由多個store組成,每個store包含一個列族的所有數據
> Store包括位于內存的memstore和位于硬盤的storefile
> 寫操作先寫入memstore,當memstore中的數據量達到某個閾值,Hregionserver啟動flashcache進程寫入storefile,每次寫入形成單獨一個storefile
> 當storefile大小超過一定閾值后,會把當前的region分割成兩個,并由Hmaster分配給相應的region服務器,實現負載均衡
> 客戶端檢索數據時,先在memstore找,找不到再找storefile
### 4、HLog(WAL log)
> WAL 意為Write ahead log(http://en.wikipedia.org/wiki/Write-ahead_logging),類似mysql中的binlog,用來 做災難恢復只用,Hlog記錄數據的所有變更,一旦數據修改,就可以從log中進行恢復。
> 每個Region Server維護一個Hlog,而不是每個Region一個。這樣不同region(來自不同table)的日志會混在一起,這樣做的目的是不斷追加單個文件相對于同時寫多個文件而言,可以減少磁盤尋址次數,因此可以提高對table的寫性能。帶來的麻煩是,如果一臺region server下線,為了恢復其上的region,需要將region server上的log進行拆分,然后分發到其它region server上進行恢復。
> HLog文件就是一個普通的Hadoop Sequence File:
1) HLog Sequence File 的Key是HLogKey對象,HLogKey中記錄了寫入數據的歸屬信息,除了table和region名字外,同時還包括 sequence number和timestamp,timestamp是”寫入時間”,sequence number的起始值為0,或者是最近一次存入文件系統中sequence number。
2) HLog Sequece File的Value是HBase的KeyValue對象,即對應HFile中的KeyValue,可參見上文描述。
## 3 尋址機制
### 1、尋址示意圖

### 2、-ROOT-和.META.表結構

> .META.行記錄結構

### 3、尋址流程
> 現在假設我們要從Table2里面插尋一條RowKey是RK10000的數據。那么我們應該遵循以下步驟:
1. 從.META.表里面查詢哪個Region包含這條數據。
2. 獲取管理這個Region的RegionServer地址。
3. 連接這個RegionServer, 查到這條數據。
> 系統如何找到某個row key (或者某個 row key range)所在的region
> bigtable 使用三層類似B+樹的結構來保存region位置。
> 第一層是保存zookeeper里面的文件,它持有root region的位置。
> 第二層root region是.META.表的第一個region其中保存了.META.表其它region的位置。通過root region,我們就可以訪問.META.表的數據。
> .META.是第三層,它是一個特殊的表,保存了hbase中所有數據表的region 位置信息。
> 說明:
1) root region永遠不會被split,保證了最需要三次跳轉,就能定位到任意region 。
2) META.表每行保存一個region的位置信息,row key 采用表名+表的最后一行編碼而成。
3) 為了加快訪問,.META.表的全部region都保存在內存中。
4) client會將查詢過的位置信息保存緩存起來,緩存不會主動失效,因此如果client上的緩存全部失效,則需要進行最多6次網絡來回,才能定位到正確的region(其中三次用來發現緩存失效,另外三次用來獲取位置信息)。
### 4 讀寫過程
#### 1 讀請求過程:
1) 客戶端通過zookeeper以及root表和meta表找到目標數據所在的regionserver
2) 聯系regionserver查詢目標數據
3) regionserver定位到目標數據所在的region,發出查詢請求
4) region先在memstore中查找,命中則返回
5) 如果在memstore中找不到,則在storefile中掃描(可能會掃描到很多的storefile----bloomfilter)
#### 2、寫請求過程:
1) client向region server提交寫請求
2) region server找到目標region
3) region檢查數據是否與schema一致
4) 如果客戶端沒有指定版本,則獲取當前系統時間作為數據版本
5) 將更新寫入WAL log
6) 將更新寫入Memstore
7) 判斷Memstore的是否需要flush為Store文件。
> 細節描述:
* hbase使用MemStore和StoreFile存儲對表的更新。
* 數據在更新時首先寫入Log(WAL log)和內存(MemStore)中,MemStore中的數據是排序的,當MemStore累計到一定閾值時,就會創建一個新的MemStore,并 且將老的MemStore添加到flush隊列,由單獨的線程flush到磁盤上,成為一個StoreFile。于此同時,系統會在zookeeper中記錄一個redo point,表示這個時刻之前的變更已經持久化了。
* 當系統出現意外時,可能導致內存(MemStore)中的數據丟失,此時使用Log(WAL log)來恢復checkpoint之后的數據。
* StoreFile是只讀的,一旦創建后就不可以再修改。因此Hbase的更新其實是不斷追加的操作。當一個Store中的StoreFile達到一定的閾值后,就會進行一次合并(minor_compact, major_compact),將對同一個key的修改合并到一起,形成一個大的StoreFile,當StoreFile的大小達到一定閾值后,又會對 StoreFile進行split,等分為兩個StoreFile。
* 由于對表的更新是不斷追加的,compact時,需要訪問Store中全部的 StoreFile和MemStore,將他們按row key進行合并,由于StoreFile和MemStore都是經過排序的,并且StoreFile帶有內存中索引,合并的過程還是比較快。
## 5 Region管理
### (1) region分配
> 任何時刻,一個region只能分配給一個region server。master記錄了當前有哪些可用的region server。以及當前哪些region分配給了哪些region server,哪些region還沒有分配。當需要分配的新的region,并且有一個region server上有可用空間時,master就給這個region server發送一個裝載請求,把region分配給這個region server。region server得到請求后,就開始對此region提供服務。
### (2) region server上線
> master使用zookeeper來跟蹤region server狀態。當某個region server啟動時,會首先在zookeeper上的server目錄下建立代表自己的znode。由于master訂閱了server目錄上的變更消息,當server目錄下的文件出現新增或刪除操作時,master可以得到來自zookeeper的實時通知。因此一旦region server上線,master能馬上得到消息。
### (3) region server下線
> 當region server下線時,它和zookeeper的會話斷開,zookeeper而自動釋放代表這臺server的文件上的獨占鎖。master就可以確定:
1) region server和zookeeper之間的網絡斷開了。
2) region server掛了。
> 無論哪種情況,region server都無法繼續為它的region提供服務了,此時master會刪除server目錄下代表這臺region server的znode數據,并將這臺region server的region分配給其它還活著的同志。
## 6 Master工作機制
### 1) master上線
> master啟動進行以下步驟:
1) 從zookeeper上獲取唯一一個代表active master的鎖,用來阻止其它master成為master。
2) 掃描zookeeper上的server父節點,獲得當前可用的region server列表。
3) 和每個region server通信,獲得當前已分配的region和region server的對應關系。
4) 掃描.META.region的集合,計算得到當前還未分配的region,將他們放入待分配region列表。
### 2) master下線
> 由于master只維護表和region的元數據,而不參與表數據IO的過程,master下線僅導致所有元數據的修改被凍結(無法創建刪除表,無法修改表的schema,無法進行region的負載均衡,無法處理region 上下線,無法進行region的合并,唯一例外的是region的split可以正常進行,因為只有region server參與),表的數據讀寫還可以正常進行。因此master下線短時間內對整個hbase集群沒有影響。
> 從上線過程可以看到,master保存的信息全是可以冗余信息(都可以從系統其它地方收集到或者計算出來)
> 因此,一般hbase集群中總是有一個master在提供服務,還有一個以上的‘master’在等待時機搶占它的位置。
> 動手練習(增刪改查)
## HBASE原理圖

## 7 HBASE參數
### 配置優化
~~~
zookeeper.session.timeout
~~~
> 默認值:3分鐘(180000ms)
> 說明:RegionServer與Zookeeper間的連接超時時間。當超時時間到后,ReigonServer會被Zookeeper從RS集群清單中移除,HMaster收到移除通知后,會對這臺server負責的regions重新balance,讓其他存活的RegionServer接管.
> 調優:
> 這個timeout決定了RegionServer是否能夠及時的failover。設置成1分鐘或更低,可以減少因等待超時而被延長的failover時間。
> 不過需要注意的是,對于一些Online應用,RegionServer從宕機到恢復時間本身就很短的(網絡閃斷,crash等故障,運維可快速介入),如果調低timeout時間,反而會得不償失。因為當ReigonServer被正式從RS集群中移除時,HMaster就開始做balance了(讓其他RS根據故障機器記錄的WAL日志進行恢復)。當故障的RS在人工介入恢復后,這個balance動作是毫無意義的,反而會使負載不均勻,給RS帶來更多負擔。特別是那些固定分配regions的場景。
~~~
hbase.regionserver.handler.count
~~~
> 默認值:10
* 說明:RegionServer的請求處理IO線程數。
> 調優:
1. 這個參數的調優與內存息息相關。
2. 較少的IO線程,適用于處理單次請求內存消耗較高的Big PUT場景(大容量單次PUT或設置了較大cache的scan,均屬于Big PUT)或ReigonServer的內存比較緊張的場景。
3. 較多的IO線程,適用于單次請求內存消耗低,TPS要求非常高的場景。設置該值的時候,以監控內存為主要參考。
4. 這里需要注意的是如果server的region數量很少,大量的請求都落在一個region上,因快速充滿memstore觸發flush導致的讀寫鎖會影響全局TPS,不是IO線程數越高越好。
5. 壓測時,開啟Enabling RPC-level logging,可以同時監控每次請求的內存消耗和GC的狀況,最后通過多次壓測結果來合理調節IO線程數。
6. 這里是一個案例?Hadoop and HBase Optimization for Read Intensive Search Applications,作者在SSD的機器上設置IO線程數為100,僅供參考。
~~~
hbase.hregion.max.filesize
~~~
> 默認值:256M
* 說明:在當前ReigonServer上單個Reigon的最大存儲空間,單個Region超過該值時,這個Region會被自動split成更小的region。
> 調優:
1. 小region對split和compaction友好,因為拆分region或compact小region里的storefile速度很快,內存占用低。缺點是split和compaction會很頻繁。
2. 特別是數量較多的小region不停地split, compaction,會導致集群響應時間波動很大,region數量太多不僅給管理上帶來麻煩,甚至會引發一些Hbase的bug。
3. 一般512以下的都算小region。
4. 大region,則不太適合經常split和compaction,因為做一次compact和split會產生較長時間的停頓,對應用的讀寫性能沖擊非常大。此外,大region意味著較大的storefile,compaction時對內存也是一個挑戰。
5. 當然,大region也有其用武之地。如果你的應用場景中,某個時間點的訪問量較低,那么在此時做compact和split,既能順利完成split和compaction,又能保證絕大多數時間平穩的讀寫性能。
6. 既然split和compaction如此影響性能,有沒有辦法去掉?
7. compaction是無法避免的,split倒是可以從自動調整為手動。
8. 只要通過將這個參數值調大到某個很難達到的值,比如100G,就可以間接禁用自動split(RegionServer不會對未到達100G的region做split)。
9. 再配合RegionSplitter這個工具,在需要split時,手動split。
10. 手動split在靈活性和穩定性上比起自動split要高很多,相反,管理成本增加不多,比較推薦online實時系統使用。
11. 內存方面,小region在設置memstore的大小值上比較靈活,大region則過大過小都不行,過大會導致flush時app的IO wait增高,過小則因store file過多影響讀性能。
~~~
hbase.regionserver.global.memstore.upperLimit/lowerLimit
~~~
> 默認值:0.4/0.35
* upperlimit說明:hbase.hregion.memstore.flush.size 這個參數的作用是當單個Region內所有的memstore大小總和超過指定值時,flush該region的所有memstore。RegionServer的flush是通過將請求添加一個隊列,模擬生產消費模式來異步處理的。那這里就有一個問題,當隊列來不及消費,產生大量積壓請求時,可能會導致內存陡增,最壞的情況是觸發OOM。
* 這個參數的作用是防止內存占用過大,當ReigonServer內所有region的memstores所占用內存總和達到heap的40%時,HBase會強制block所有的更新并flush這些region以釋放所有memstore占用的內存。
* lowerLimit說明: 同upperLimit,只不過lowerLimit在所有region的memstores所占用內存達到Heap的35%時,不flush所有的memstore。它會找一個memstore內存占用最大的region,做個別flush,此時寫更新還是會被block。lowerLimit算是一個在所有region強制flush導致性能降低前的補救措施。在日志中,表現為 “** Flush thread woke up with memory above low water.”
> 調優:
* 這是一個Heap內存保護參數,默認值已經能適用大多數場景。
* 參數調整會影響讀寫,如果寫的壓力大導致經常超過這個閥值,則調小讀緩存hfile.block.cache.size增大該閥值,或者Heap余量較多時,不修改讀緩存大小。
* 如果在高壓情況下,也沒超過這個閥值,那么建議你適當調小這個閥值再做壓測,確保觸發次數不要太多,然后還有較多Heap余量的時候,調大hfile.block.cache.size提高讀性能。
* 還有一種可能性是?hbase.hregion.memstore.flush.size保持不變,但RS維護了過多的region,要知道 region數量直接影響占用內存的大小。
hfile.block.cache.size
> 默認值:0.2
* 說明:storefile的讀緩存占用Heap的大小百分比,0.2表示20%。該值直接影響數據讀的性能。
* 調優:當然是越大越好,如果寫比讀少很多,開到0.4-0.5也沒問題。如果讀寫較均衡,0.3左右。如果寫比讀多,果斷默認吧。設置這個值的時候,你同時要參考?
> hbase.regionserver.global.memstore.upperLimit?,該值是memstore占heap的最大百分比,兩個參數一個影響讀,一個影響寫。如果兩值加起來超過80-90%,會有OOM的風險,謹慎設置。
~~~
hbase.hstore.blockingStoreFiles
~~~
默認值:7
* 說明:在flush時,當一個region中的Store(Coulmn Family)內有超過7個storefile時,則block所有的寫請求進行compaction,以減少storefile數量。
* 調優:block寫請求會嚴重影響當前regionServer的響應時間,但過多的storefile也會影響讀性能。從實際應用來看,為了獲取較平滑的響應時間,可將值設為無限大。如果能容忍響應時間出現較大的波峰波谷,那么默認或根據自身場景調整即可。
~~~
hbase.hregion.memstore.block.multiplier
~~~
> 默認值:2
* 說明:當一個region里的memstore占用內存大小超過hbase.hregion.memstore.flush.size兩倍的大小時,block該region的所有請求,進行flush,釋放內存。雖然我們設置了region所占用的memstores總內存大小,比如64M,但想象一下,在最后63.9M的時候,我Put了一個200M的數據,此時memstore的大小會瞬間暴漲到超過預期的hbase.hregion.memstore.flush.size的幾倍。這個參數的作用是當memstore的大小增至超過hbase.hregion.memstore.flush.size 2倍時,block所有請求,遏制風險進一步擴大。
* 調優: 這個參數的默認值還是比較靠譜的。如果你預估你的正常應用場景(不包括異常)不會出現突發寫或寫的量可控,那么保持默認值即可。如果正常情況下,你的寫請求量就會經常暴長到正常的幾倍,那么你應該調大這個倍數并調整其他參數值,比如
> hfile.block.cache.sizehbase.regionserver.global.memstore.upperLimit/lowerLimit,以預留更多內存,防止HBase server OOM。
~~~
hbase.hregion.memstore.mslab.enabled
~~~
> 默認值:true
* 說明:減少因內存碎片導致的Full GC,提高整體性能。
* 調優:詳見 http://kenwublog.com/avoid-full-gc-in-hbase-using-arena-allocation
### 其他
#### 啟用LZO壓縮
> LZO對比Hbase默認的GZip,前者性能較高,后者壓縮比較高,具體參見?Using LZO Compression 。對于想提高HBase讀寫性能的開發者,采用LZO是比較好的選擇。對于非常在乎存儲空間的開發者,則建議保持默認。
> 不要在一張表里定義太多的Column Family
> Hbase目前不能良好的處理超過包含2-3個CF的表。因為某個CF在flush發生時,它鄰近的CF也會因關聯效應被觸發flush,最終導致系統產生更多IO。
> 批量導入
> 在批量導入數據到Hbase前,你可以通過預先創建regions,來平衡數據的負載。詳見?Table Creation: Pre-Creating Regions
> 避免CMS concurrent mode failure
> HBase使用CMS GC。默認觸發GC的時機是當年老代內存達到90%的時候,這個百分比由 -XX:CMSInitiatingOccupancyFraction=N 這個參數來設置。concurrent mode failed發生在這樣一個場景:
當年老代內存達到90%的時候,CMS開始進行并發垃圾收集,于此同時,新生代還在迅速不斷地晉升對象到年老代。當年老代CMS還未完成并發標記時,年老代滿了,悲劇就發生了。CMS因為沒內存可用不得不暫停mark,并觸發一次stop the world(掛起所有jvm線程),然后采用單線程拷貝方式清理所有垃圾對象。這個過程會非常漫長。為了避免出現concurrent mode failed,建議讓GC在未到90%時,就觸發。
通過設置?-XX:CMSInitiatingOccupancyFraction=N
這個百分比, 可以簡單的這么計算。如果你的?hfile.block.cache.size 和?hbase.regionserver.global.memstore.upperLimit 加起來有60%(默認),那么你可以設置 70-80,一般高10%左右差不多。
### Hbase客戶端優化
~~~
AutoFlush
~~~
* 將HTable的setAutoFlush設為false,可以支持客戶端批量更新。即當Put填滿客戶端flush緩存時,才發送到服務端。
* 默認是true。
~~~
Scan Caching
~~~
* scanner一次緩存多少數據來scan(從服務端一次抓多少數據回來scan)。
* 默認值是 1,一次只取一條。
~~~
Scan Attribute Selection
~~~
* scan時建議指定需要的Column Family,減少通信量,否則scan操作默認會返回整個row的所有數據(所有Coulmn Family)。
~~~
Close ResultScanners
~~~
* 通過scan取完數據后,記得要關閉ResultScanner,否則RegionServer可能會出現問題(對應的Server資源無法釋放)。
~~~
Optimal Loading of Row Keys
~~~
* 當你scan一張表的時候,返回結果只需要row key(不需要CF, qualifier,values,timestaps)時,你可以在scan實例中添加一個filterList,并設置 MUST_PASS_ALL操作,filterList中add?FirstKeyOnlyFilter或KeyOnlyFilter。這樣可以減少網絡通信量。
~~~
Turn off WAL on Puts
~~~
* 當Put某些非重要數據時,你可以設置writeToWAL(false),來進一步提高寫性能。writeToWAL(false)會在Put時放棄寫WAL log。風險是,當RegionServer宕機時,可能你剛才Put的那些數據會丟失,且無法恢復。
~~~
啟用Bloom Filter
~~~
* Bloom Filter通過空間換時間,提高讀操作性能。
## 8 HBASE性能優化
1) hbase.hregion.max.filesize應該設置多少合適
2) autoflush=false的影響
3) 從性能的角度談table中family和qualifier的設置
4) hbase.regionserver.handler.count詳解
### 1 hbase.hregion.max.filesize應該設置多少合適
~~~
默認值:256M
~~~
> 說明:
* Maximum HStoreFile size. If any one of a column families' HStoreFiles has grown to exceed this value, the hosting HRegion is split in two.
* HStoreFile的最大值。如果任何一個Column Family(或者說HStore)的HStoreFiles的大小超過這個值,那么,其所屬的HRegion就會Split成兩個。
> 調優:
* hbase中hfile的默認最大值(hbase.hregion.max.filesize)是256MB,而google的bigtable論文中對tablet的最大值也推薦為100-200MB,這個大小有什么秘密呢?
* 眾所周知hbase中數據一開始會寫入memstore,當memstore滿64MB以后,會flush到disk上而成為storefile。當storefile數量超過3時,會啟動compaction過程將它們合并為一個storefile。這個過程中會刪除一些timestamp過期的數據,比如update的數據。而當合并后的storefile大小大于hfile默認最大值時,會觸發split動作,將它切分成兩個region。
* lz進行了持續insert壓力測試,并設置了不同的hbase.hregion.max.filesize,根據結果得到如下結論:值越小,平均吞吐量越大,但吞吐量越不穩定;值越大,平均吞吐量越小,吞吐量不穩定的時間相對更小。
> 為什么會這樣呢?推論如下:
1) 當hbase.hregion.max.filesize比較小時,觸發split的機率更大,而split的時候會將region offline,因此在split結束的時間前,訪問該region的請求將被block住,客戶端自我block的時間默認為1s。當大量的region同時發生split時,系統的整體訪問服務將大受影響。因此容易出現吞吐量及響應時間的不穩定現象
2) 當hbase.hregion.max.filesize比較大時,單個region中觸發split的機率較小,大量region同時觸發split的機率也較小,因此吞吐量較之小hfile尺寸更加穩定些。但是由于長期得不到split,因此同一個region內發生多次compaction的機會增加了。compaction的原理是將原有數據讀一遍并重寫一遍到hdfs上,然后再刪除原有數據。無疑這種行為會降低以io為瓶頸的系統的速度,因此平均吞吐量會受到一些影響而下降。
> 綜合以上兩種情況,hbase.hregion.max.filesize不宜過大或過小,256MB或許是一個更理想的經驗參數。對于離線型的應用,調整為128MB會更加合適一些,而在線應用除非對split機制進行改造,否則不應該低于256MB
### 2 autoflush=false的影響
> 無論是官方還是很多blog都提倡為了提高hbase的寫入速度而在應用代碼中設置autoflush=false,然后lz認為在在線應用中應該謹慎進行該設置。原因如下:
1) autoflush=false的原理是當客戶端提交delete或put請求時,將該請求在客戶端緩存,直到數據超過2M(hbase.client.write.buffer決定)或用戶執行了hbase.flushcommits()時才向regionserver提交請求。因此即使htable.put()執行返回成功,也并非說明請求真的成功了。假如還沒有達到該緩存而client崩潰,該部分數據將由于未發送到regionserver而丟失。這對于零容忍的在線服務是不可接受的。
2) autoflush=true雖然會讓寫入速度下降2-3倍,但是對于很多在線應用來說這都是必須打開的,也正是hbase為什么讓它默認值為true的原因。當該值為true時,每次請求都會發往regionserver,而regionserver接收到請求后第一件事就是寫hlog,因此對io的要求是非常高的,為了提高hbase的寫入速度,應該盡可能高地提高io吞吐量,比如增加磁盤、使用raid卡、減少replication因子數等
### 3 從性能的角度談table中family和qualifier的設置
> 對于傳統關系型數據庫中的一張table,在業務轉換到hbase上建模時,從性能的角度應該如何設置family和qualifier呢?
> 最極端的,①每一列都設置成一個family,②一個表僅有一個family,所有列都是其中的一個qualifier,那么有什么區別呢?
> 從讀的方面考慮:
* family越多,那么獲取每一個cell數據的優勢越明顯,因為io和網絡都減少了。
* 如果只有一個family,那么每一次讀都會讀取當前rowkey的所有數據,網絡和io上會有一些損失。
* 當然如果要獲取的是固定的幾列數據,那么把這幾列寫到一個family中比分別設置family要更好,因為只需一次請求就能拿回所有數據。
> 從寫的角度考慮:
1) 內存方面來說,對于一個Region,會為每一個表的每一個Family分配一個Store,而每一個Store,都會分配一個MemStore,所以更多的family會消耗更多的內存。
2) 從flush和compaction方面說,目前版本的hbase,在flush和compaction都是以region為單位的,也就是說當一個family達到flush條件時,該region的所有family所屬的memstore都會flush一次,即使memstore中只有很少的數據也會觸發flush而生成小文件。這樣就增加了compaction發生的機率,而compaction也是以region為單位的,這樣就很容易發生compaction風暴從而降低系統的整體吞吐量。
3) 從split方面考慮,由于hfile是以family為單位的,因此對于多個family來說,數據被分散到了更多的hfile中,減小了split發生的機率。這是把雙刃劍。更少的split會導致該region的體積比較大,由于balance是以region的數目而不是大小為單位來進行的,因此可能會導致balance失效。而從好的方面來說,更少的split會讓系統提供更加穩定的在線服務。而壞處我們可以通過在請求的低谷時間進行人工的split和balance來避免掉。
> 因此對于寫比較多的系統,如果是離線應該,我們盡量只用一個family好了,但如果是在線應用,那還是應該根據應用的情況合理地分配family。
### 4 hbase.regionserver.handler.count
* RegionServer端開啟的RPC監聽器實例個數,也即RegionServer能夠處理的IO請求線程數。默認是10.
* 此參數與內存息息相關。該值設置的時候,以監控內存為主要參考。
* 對于 單次請求內存消耗較高的Big PUT場景(大容量單次PUT或設置了較大cache的scan,均屬于Big PUT)或ReigonServer的內存比較緊張的場景,可以設置的相對較小。
* 對于 單次請求內存消耗低,TPS(TransactionPerSecond,每秒事務處理量)要求非常高的場景,可以設置的相對大些。
- hadoop
- linux基礎
- Linux入門
- Linux進階
- shell
- Zookeeper
- Zookeeper簡介及部署
- Zookeeper使用及API
- Redis
- Redis簡介安裝部署
- Redis使用及API
- Java高級增強
- Java多線程增強
- Maven簡介及搭建
- Hive
- Hive簡介及安裝
- Hive操作
- HIve常用函數
- Hive數據類型
- Flume
- Flume簡介及安裝
- flume 攔截器(interceptor)
- azkaban
- azKaban簡介及安裝
- Sqoop
- Sqoop簡介及安裝
- HDFS
- HDFS原理
- HDFS操作API
- MAPREDUCE原理
- MAPREDUCE圖片資源
- MAPREDUCE加強
- HBASE
- HBASE簡介及安裝
- HBASE操作及API
- HBASE內部原理
- Storm
- Storm簡介及安裝
- Storm原理
- kafka
- kafka簡介及安裝
- kafka常用操作及API
- kafka原理
- kafka配置詳解
- Scala
- Scala簡介及安裝
- Scala基礎語法
- Scala實戰