天天看點

HBase寫性能優化

入門級的調優可以從調整參數開始。投入小,回報快。

快速配置

設定buffer的容量,例子中設定了6mb的buffer容量。

必須禁止auto flush。

6mb是經驗值,可以上下微調以适應不同的寫場景。

原理

hbase client會在資料累積到設定的門檻值後才送出region server。這樣做的好處在于可以減少rpc連接配接次數。同時,我們得計算一下服務端是以而消耗的記憶體:hbase.client.write.buffer * hbase.regionserver.handler.count。在減少prc次數和增加伺服器端記憶體之間找到平衡點。

修改hbase-site.xml的hbase.regionserver.handler.count配置項:

hbase.regionserver.handler.count

100

該配置定義了每個region server上的rpc handler的數量。region server通過rpc handler接收外部請求并加以處理。是以提升rpc handler的數量可以一定程度上提高hbase接收請求的能力。當然,handler數量也不是越大越好,這要取決于節點的硬體情況。

hcolumndescriptor hcd = new hcolumndescriptor(familyname);

hcd.setcompressiontype(algorithm.snappy);

資料量大,邊壓邊寫也會提升性能的,畢竟io是大資料的最嚴重的瓶頸,哪怕使用了ssd也是一樣。衆多的壓縮方式中,推薦使用snappy。從壓縮率和壓縮速度來看,成本效益最高。

put put = new put(rowkey);

put.setwritetowal(false);

其實不推薦關閉wal,不過關了的确可以提升性能...因為hbase在寫資料前會先寫wal,以保證在異常情況下,hbase可以按照wal的記錄來恢複還未持久化的資料。

雖然推薦replica=3,不過當資料量很誇張的時候,一般會把replica降低到2。當然也不推薦随便降低replica。

在插資料時,打開hmaster的web界面,檢視每個region server的request數量。確定大部分時間,寫請求在region server層面大緻平均分布。

在此前提下,我們再考慮compaction的問題。繼續觀察request數量,你會發現在某個時間段,若幹region server接收的請求數為0(當然這也可能是client根本沒有向這個region server寫資料,是以之前說,要確定請求在各region server大緻平均分布)。這很有可能是region server在做compaction導緻。compaction的過程會block寫。

優化的思路有兩種,一是提高compaction的效率,二是減少compaction發生的頻率。

提高以下兩個屬性的值,以增加執行compaction的線程數:

hbase.regionserver.thread.compaction.large

hbase.regionserver.thread.compaction.small

推薦設定為2。

region split是提升寫性能的一大障礙。減少region split次數可以從兩方面入手,一是預配置設定region(該内容會在下章節表設計優化裡詳述)。其二是适當提升hbase.hregion.max.filesize

提升region的file容量也可以減少split的次數。具體的值需要按照你的資料量,region數量,row key分布等情況具體考量。一般來說,3~4g是不錯的選擇。

0.92.0後的version都應該是2。v2比v1支援更大的region大小。一般經驗是region越大越少,性能更好(當然也不能過分大,否則major compaction的時候時間長的吃不消)。是以推薦把hfile.format.version改成2,并提高hfile大小。對于使用v1 format的使用者,不用擔心,資料遷移到v2上是有工具的。具體參見hbase-1621。

設定成true。關閉nagle,可能提高latency。當然hdfs也關掉tpc nagle。

a tcp/ip optimization called the nagle algorithm can also limit data transfer speed on a connection. the nagle algorithm is designed to reduce protocol overhead for applications that send small amounts of data, such as telnet, which sends a single character at a time. rather than immediately send a packet with lots of header and little data, the stack waits for more data from the application, or an acknowledgment, before proceeding.

之前有說防止region split的兩大手段其中之一就是預配置設定region。

實測發現column family的數量對性能會有直接影響。建議減少column family的數量。單個cf是最好

前者确定儲存一個cell的最大曆史份數,後者确定多少byte可以存進一個cell 曆史記錄。是以我們可以減低這些值。

region的資料邊界是start key和end key。如果記錄的row key落在某個region的start key和end key的範圍之内,該資料就會存儲到這個region上。在寫資料的時候,尤其是導入客戶原有資料的時候,如果row key設計不當,很可能導緻性能問題。之前我們也介紹了row key和region的關系。如果在某個時段内,很多資料的row key都處在某個特定的row key範圍内。那這個特定範圍row key對應的region會非常繁忙,而其他的region很可能非常的空閑,導緻資源浪費。

那麼,如何設計row key呢?舉個比較實際的例子,如果有張hbase表來記錄每天某城市的通話記錄, 正常思路下的row key是由電話号碼 + yyyymmddhhmmss(通話開始時間) + ... 組成。按電話号碼的規律來劃分region。但是這樣很容易導緻某時段row key極其不均勻(因為電話通話呈随機性)。但是,如果把電話号碼倒序,資料在region層面的分布情況就大有改觀。

設計row key的方法千變萬化,宗旨隻有一條,盡量保證機關時間内寫入資料的row key對于region呈均勻分布。

實踐發現,寫性能差大部分情況是源于client端的糟糕設計。接下來分享一些client設計的思路。

之前也提到了rpc handler的概念。好的data loader需要保證每個rpc handlder都有活幹,每個handler忙,但不至超載。注意region的壓力不能過大,否則會導緻反複重試,并伴有逾時異常(可以提高逾時的時間設定)。

如何保證每個region server的壓力均衡呢?這和region 數量,row key的設計 和client資料的插入順序有關。設計者需要根據使用者資料的情況,叢集情況來綜合考慮。

多線程是最簡單的解決方案。要點是讓每個線程負責一部分的row key範圍,而row key範圍又和region相關,是以可以在資料插入時,程式控制每個region的壓力,不至于有些region閑着沒事幹。由于相對簡單,不再贅述。

即使使用多線程,也受限于單節點的硬體資源,寫入速度不可能很快。典型的思路是将用戶端部署在多個節點上運作,提高寫的并發度。mapreduce是個很好的選擇。使用mapreduce把寫入程式分布到叢集的各個節點上,并在每個mapper中運作多線程的插入程式。這樣可以很好的提高寫并發度。

注意,不要使用reducer。mapper到reducer需要走網絡,受限于叢集帶寬。其次,實際的應用場景一般是使用者從關系型資料庫中導出了文本類型的資料,然後希望能把導出的資料寫到hbase裡。在這種情況下,需要小心謹慎地設計和實作fileinputformat的file split邏輯。

請拿出hbase的api讀讀,hfileoutputfomart裡有個叫configureincrementalload的方法。api是這麼介紹的:

configure a mapreduce job to perform an incremental load into the given table. this

inspects the table to configure a total order partitioner

uploads the partitions file to the cluster and adds it to the distributedcache

sets the number of reduce tasks to match the current number of regions

sets the output key/value class to match hfileoutputformat's requirements

sets the reducer up to perform the appropriate sorting (either keyvaluesortreducer or putsortreducer)

the user should be sure to set the map output value class to either keyvalue or put before running this function.

這是hbase提供的一種基于mapreduce的資料導入方案,完美地繞過了hbase client(上一節的分布式插入方法也是用mapreduce實作的,不過本質上還是用hbase client來寫資料)

網上有不少文章叙述了使用指令行方式運作bulkload,google一下你就知道...

但是,不得不說,實際生産環境上很難使用這種方式。畢竟源資料不可能直接用來寫hbase。在資料遷移的過程中會涉及到資料清洗、整理歸并等許多額外的工作。這顯然不是指令行可以做到的事情。按照api的描述, 可行的方案是自定義一個mapper在mapper中清洗資料,mapper的輸出value為hbase的put類型,reducer選用putsortreducer。然後使用hfileoutputformat#configureincrementalload(job, htable);解決剩餘工作。

不過,這種實作也存在局限性。畢竟mapper到reducer比較吃網絡。

繼續閱讀