實時系統HBase讀寫優化--大量寫入無障礙
在使用hbase過程中發現在寫入hbase的資料量很大時,經常發生寫不進去的情況。而我們基於hbase的應用是對實時性要求很高的,一旦hbase不能讀寫則會大大影響系統的使用。下面將記錄hbase寫優化的過程。
1.禁止Major Compaction
在hbase進行Major Compaction時,該region將合併所有的storefile,因此整個region都不可讀,所有對此region的查詢都會block。HBase預設一天左右執行一次Major Compaction。我們將Major Compaction禁掉並用Cron指令碼每天在系統空閒時對所有表執行major compaction。
Major Compaction的配置:
- <spanstyle="font-size:18px;"><property>
- <name>hbase.hregion.majorcompaction</name>
- <value>0</value>
- </property></span>
預設是1天,每個region會在建立時以當前時間初始化regionMajorCompactionTime,並將下一次的major compaction時間設為1+-0.2天。配置中將此值設為0禁止major compaction。
major_compaction的指令碼:取出所有table,一一執行major_compact:
- <span style="font-size:18px;">TMP_FILE=tmp_tables
- TABLES_FILE=tables.txt
- echo "list" | hbase shell > tmp_tables
- sleep 2
- sed '1,6d' $TMP_FILE | tac | sed '1,2d' | tac > $TABLES_FILE
- sleep 2
-
for table in $(cat $TABLES_FILE);
- echo "major_compact '$table'" | hbase shell
- sleep 10
- done</span>
hbase通過split region實現水平的sharding,但在split的過程中舊的region會下線,新region還會做compaction,中間有一段時間大量的資料不能被讀寫,這對於我們這種online系統是不能忍受的。我們同樣禁掉自動的split,而在晚上系統空閒時執行我們的splittool手動的split。
禁止split的配置:
- <spanstyle="font-size:18px;"><property>
- <name>hbase.hregion.max.filesize</name>
- <value>536870912000</value>
- </property></span>
splittool的邏輯比較簡單。遍歷所有region的資訊,如果region大小大於某值(比如1G)則split該region,這樣為一輪split,如果一輪後沒有大於某值的region則結束,如果還有大於某個值的region則繼續新一輪split,直到沒有region大於某個閾值為止。這裡提一下判斷split完成的方法:通過檢查hdfs上舊region的資料夾是否被清除來判斷split是否結束。
3.設定blockingStoreFiles
這個引數的重要性是在我們的效能測試中發現的。我們禁掉major_compaction和split後理論上寫入應該無障礙了,但在測試中發現寫入單個region速度大於10M/s時還是會出現長時間無法寫入的情況。通過檢視log,我們發現了這行log“Waited 90314ms on a compaction to clean up 'too many store files'”,通過檢視程式碼發現原來是blockingStoreFiles這個引數在作怪。
在flushRegion時會檢測當前store中hfile的數量是否大於此值,如果大於則會block資料的寫入,等待其他執行緒將hfile compact掉。這樣,如果寫入速度超過compact的速度,hbase就會阻止該region的資料寫入。
- <span style="font-size:18px;">privateboolean flushRegion(final FlushRegionEntry fqe) {
- HRegion region = fqe.region;
- if (!fqe.region.getRegionInfo().isMetaRegion() &&
- isTooManyStoreFiles(region)) {
- if (fqe.isMaximumWait(this.blockingWaitTime)) {
- LOG.info("Waited " + (System.currentTimeMillis() - fqe.createTime) +
- "ms on a compaction to clean up 'too many store files'; waited " +
- "long enough... proceeding with flush of " +
- region.getRegionNameAsString());
- } </span>
- <span style="font-size:18px;">this.blockingStoreFilesNumber =
- conf.getInt("hbase.hstore.blockingStoreFiles", 7);
- if (this.blockingStoreFilesNumber == -1) {
- this.blockingStoreFilesNumber = 1 +
- conf.getInt("hbase.hstore.compactionThreshold", 3);
- }</span>
- <spanstyle="font-size:18px;"><property>
- <name>hbase.hstore.blockingStoreFiles</name>
- <value>2100000000</value>
- </property></span>