優(yōu)化hbase的查詢提升讀寫(xiě)速率優(yōu)化案例及性能提升的幾種方法

在初期,我們采用的邏輯是:將A表中的數(shù)據(jù)讀取一行,根據(jù)其中的某個(gè)字段去組織一個(gè)GET,然后立刻提交,從B表取得要查詢的字段的值,組織成一個(gè)PUT,并提交到A表。那么這么做的話,
1.完全發(fā)揮不出hbase的效率的原因是什么?


2.使用bloomfilter和mapfile_index_interval如何提升性能?
3.如何設(shè)置hbase的內(nèi)存?
4.如何增大RPC的數(shù)量?
擴(kuò)展:

5.為什么HBase是基于列模式的存儲(chǔ)?

image

1.優(yōu)化案例

環(huán)境:suse 8G內(nèi)存,8核,12T磁盤
hbase master 占一臺(tái),其他7臺(tái)作為hbase的region server
注意:此處不討論hadoop

情景:
我們有7億的數(shù)據(jù),需要做查詢操作,需要從1.7億的表中查找一個(gè)字段,并寫(xiě)入到7億數(shù)據(jù)的表中。
這里為了描述方便,將7億數(shù)據(jù)的表稱為:A表,1.7億數(shù)據(jù)的表稱為B表。

      在初期,我們采用的邏輯是:將A表中的數(shù)據(jù)讀取一行,根據(jù)其中的某個(gè)字段去組織一個(gè)GET,然后
      立刻提交,從B表取得要查詢的字段的值,組織成一個(gè)PUT,并提交到A表。
      那么這么做的話,完全發(fā)揮不出hbase的效率,因?yàn)槊總€(gè)get之間它的key不一定連續(xù),或者說(shuō)是在同一范圍
      而hbase的服務(wù)端會(huì)根據(jù)每個(gè)請(qǐng)求去加載數(shù)據(jù)到內(nèi)存,由于請(qǐng)求的塊分散,那么數(shù)據(jù)在內(nèi)存中的替換過(guò)多的頻繁。
      很有可能和直接讀取磁盤數(shù)據(jù)差不多。
      并且采用這種邏輯去處理數(shù)據(jù),需要花費(fèi)時(shí)間太多。差不多是10W行讀寫(xiě)數(shù)據(jù)需要40分鐘。這對(duì)于A表的更新操作
      完全是不能接受的。

      之后,通過(guò)讀數(shù)據(jù)的讀取操作進(jìn)行封裝,組織成一個(gè)ArrayList<Get> 當(dāng)?shù)揭欢ǔ潭鹊臅r(shí)候采取提交。這里還有一個(gè)情況就是
      有些數(shù)據(jù)查詢不到,那么需要去連接數(shù)據(jù)庫(kù)去申請(qǐng)一個(gè)自動(dòng)分配的值,并立刻提交,因?yàn)楹竺婵赡苡姓?qǐng)求這個(gè)數(shù)據(jù)。
      這就需要分開(kāi)處理。
      在組織GET 列表的時(shí)候需要先查詢,注意,不要采用table.get去取一個(gè)cell的值并判斷是否為null來(lái)處理。
      而是用table.exist(get) 去查詢,這是在server-side跑的,效率要高很多。
      對(duì)于查詢不到的值立刻申請(qǐng)值并寫(xiě)入A表。
      對(duì)于查詢的到的,那么就可前面說(shuō)的組織get 加入到GET列表中,到一定程度再去一次提交,在取到值之后,
      在根據(jù)將循環(huán)數(shù)據(jù)的記錄,將這些組織成put,數(shù)量和GET列表一樣,不要去具體指定,在循環(huán)一次后直接table.put

      其他參數(shù)修改方面,寫(xiě)的都很多,這里就不提了。

處理速度(取至其中一臺(tái)服務(wù)器中跑的任務(wù)):
2011-12-30 17:10:03 Start Write Lines:1700000
2011-12-30 17:14:10 Writed Lines:1700000
2011-12-30 17:14:11 Start Write Lines:1800000
2011-12-30 17:18:21 Writed Lines:1800000
2011-12-30 17:18:22 Start Write Lines:1900000
2011-12-30 17:22:29 Writed Lines:1900000
2011-12-30 17:22:29 Start Write Lines:2000000
2011-12-30 17:26:37 Writed Lines:2000000
2011-12-30 17:26:37 Start Write Lines:2100000

大約是查詢,寫(xiě)入速度是4分鐘處理10W行數(shù)據(jù)。

也就是4000/s的速率,較之前的處理方式提升了一個(gè)量級(jí)

image

性能提升方法

1、使用bloomfilter和mapfile_index_interval

|

Bloomfilter(開(kāi)啟/未開(kāi)啟=1/0)

|

mapfile_index_interval

|

Exists(0-10000)/ms

|

Get(10001 - 20000)/ms

|
|

0

|

128

|

22460

|

23715

|
|

0

|

0

|

11897

|

11416

|
|

0

|

64

|

13692

|

14034

|
|

1

|

128

|

3275

|

3686

|
|

1

|

64

|

2961

|

3010

|
|

1

|

0

|

3339

|

3498

|

測(cè)試環(huán)境為:單機(jī),規(guī)模為10萬(wàn)條數(shù)據(jù)。隨機(jī)在10000條數(shù)據(jù)中有99條存在的情況下。

結(jié)論:開(kāi)啟bloomfilter比沒(méi)開(kāi)啟要快3、4倍。而適當(dāng)?shù)臏p少mapfile_index_interval可以提升性能

注意:在1.9.3版本的hbase中,bloomfilter是不支持的,存在一個(gè)bug,可以通過(guò)如下的修改加以改正:
(1)、在方法org.apache.hadoop.hbase.regionserver.HStore.createReaders()中,找到如下行
BloomFilterMapFile.Reader reader = file.getReader(fs, false, false);
將其改成
BloomFilterMapFile.Reader reader = file.getReader(fs, this.family.isBloomfilter(), false);
(2)、在方法org.apache.hadoop.hbase.HColumnDescriptor.toString()中,找到如下的代碼行
if (key != null && key.toUpperCase().equals(BLOOMFILTER)) {
// Don't emit bloomfilter. Its not working.
continue;
}
將其注釋掉

2、hbase對(duì)于內(nèi)存有特別的嗜好,在硬件允許的情況下配足夠多的內(nèi)存給它。
通過(guò)修改hbase-env.sh中的
export HBASE_HEAPSIZE=3000 #這里默認(rèn)為1000m

3、修改java虛擬機(jī)屬性
(1)、在環(huán)境允許的情況下?lián)Q64位的虛擬機(jī)
(2)、替換掉默認(rèn)的垃圾回收器,因?yàn)槟J(rèn)的垃圾回收器在多線程環(huán)境下會(huì)有更多的wait等待
export HBASE_OPTS="-server -XX:NewSize=6m -XX:MaxNewSize=6m -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode"

4、增大RPC數(shù)量
通過(guò)修改hbase-site.xml中的
hbase.regionserver.handler.count屬性,可以適當(dāng)?shù)姆糯蟆DJ(rèn)值為10有點(diǎn)小

5、做程序開(kāi)發(fā)是注意的地方
(1)、需要判斷所求的數(shù)據(jù)行是否存在時(shí),盡量不要用HTable.exists(final byte [] row) 而用HTable.exists(final byte [] row, final byte[] column)等帶列族的方法替代。
(2)、不要使用HTable.get(final byte [] row, final byte [] column) == null來(lái)判斷所求的數(shù)據(jù)存在,而是用HTable.exists(final byte [] row, final byte[] column)替代
(3)、HTable.close()方法少用.因?yàn)槲矣龅竭^(guò)一些很令人費(fèi)解的錯(cuò)誤

6、記住HBase是基于列模式的存儲(chǔ),如果一個(gè)列族能搞定就不要把它分開(kāi)成兩個(gè),關(guān)系數(shù)據(jù)庫(kù)的那套在這里很不實(shí)用.分成多個(gè)列來(lái)存儲(chǔ)會(huì)浪費(fèi)更多的空間,除非你認(rèn)為現(xiàn)在的硬盤和白菜一個(gè)價(jià)。

7、如果數(shù)據(jù)量沒(méi)有達(dá)到TB級(jí)別或者沒(méi)有上億條記錄,很難發(fā)揮HBase的優(yōu)勢(shì),建議換關(guān)系數(shù)據(jù)庫(kù)或別的存儲(chǔ)技術(shù)。

推薦資源:HBase性能深度分析和性能調(diào)優(yōu)

上文提到了Bloom Filter,這里補(bǔ)充一下:
Bloom Filter原理及在Hbase上的應(yīng)用

首先最重要的是要搞清楚Bloom Filter是做什么的,看了幾篇文章的表述,其實(shí)最根本的就是:

判斷一個(gè)元素是否屬于這個(gè)集合。

如果這個(gè)集合中的元素足夠多,那么通過(guò)傳統(tǒng)方法進(jìn)行如上判斷耗時(shí)會(huì)很多,Bloom Filter就是一種利用很少的空間換取時(shí)間的非常實(shí)用的方法。但是要說(shuō)明的是:Bloom Filter的這種高效是有一定代價(jià)的,在判斷一個(gè)元素是否屬于某個(gè)集合時(shí),有可能會(huì)把不屬于這個(gè)集合的元素誤認(rèn)為屬于這個(gè)集合(false positive)。因此,Bloom Filter不適合那些“零錯(cuò)誤”的應(yīng)用場(chǎng)合。而在能容忍低錯(cuò)誤率的應(yīng)用場(chǎng)合下,Bloom Filter通過(guò)極少的錯(cuò)誤換取了存儲(chǔ)空間的極大節(jié)省。

下面描述下Bloom Filter的原理:

Bloom Filter是m位的數(shù)組,且這m個(gè)數(shù)組的每一位都是零。

image

Step 1 映射:假如我們有A={x1,x2,x3….xn} n個(gè)元素,那么我們需要k個(gè)相互獨(dú)立的哈希函數(shù),將其中每個(gè)元素進(jìn)行k次哈希,他們分別將這個(gè)元素映射到m位的數(shù)組中,而其映射的位置就置為1,如果有重復(fù)的元素映射到這個(gè)數(shù)組的同一個(gè)元素,那么這個(gè)元素只會(huì)記錄一次1,后續(xù)的映射將不會(huì)改變的這個(gè)元素的值。如圖:

image

Step 2 判斷:在判斷B={y1,y2} 這兩個(gè)元素是否屬于A集合時(shí),我們就將這兩個(gè)元素分別進(jìn)行上步映射中的k個(gè)哈希函數(shù)的哈希,如果結(jié)果全為1,那么就判斷屬于A集合,否則判斷其不屬于A集合。如下圖 y2屬于,y1則不屬于。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容