1. 程式人生 > >hbase 寫資料,存資料,讀資料的詳細過程

hbase 寫資料,存資料,讀資料的詳細過程

Client寫入 -> 存入MemStore,一直到MemStore滿 -> Flush成一個StoreFile,直至增長到一定閾值 -> 出發Compact合併操作 -> 多個StoreFile合併成一個StoreFile,同時進行版本合併和資料刪除 -> 當StoreFiles Compact後,逐步形成越來越大的StoreFile -> 單個StoreFile大小超過一定閾值後,觸發Split操作,把當前Region Split成2個Region,Region會下線,新Split出的2個孩子Region會被HMaster分配到相應的HRegionServer 上。

Hbase寫資料和存資料的過程

Hbase寫資料,存資料,讀資料的詳細過程

Client寫入 -> 存入MemStore,一直到MemStore滿 -> Flush成一個StoreFile,直至增長到一定閾值 -> 出發Compact合併操作 -> 多個StoreFile合併成一個StoreFile,同時進行版本合併和資料刪除 -> StoreFiles Compact後,逐步形成越來越大的StoreFile -> 單個StoreFile大小超過一定閾值後,觸發Split操作,把當前Region Split2RegionRegion會下線,新Split出的2個孩子Region會被HMaster

分配到相應的HRegionServer 上,使得原先1Region的壓力得以分流到2Region上由此過程可知,HBase只是增加資料,有所得更新和刪除操作,都是在Compact階段做的,所以,使用者寫操作只需要進入到記憶體即可立即返回,從而保證I/O高效能。

對上述流程的補充:

補充1HStore儲存是HBase儲存的核心,其中由兩部分組成,一部分是MemStore,一部分是StoreFiles

補充2HLog的功能:

在分散式系統環境中,無法避免系統出錯或者宕機,一旦HRegionServer以外退出,

MemStore中的記憶體資料就會丟失,引入HLog就是防止這種情況。

工作機制:每 個HRegionServer中都會有一個HLog物件,HLog是一個實現Write Ahead Log的類,每次使用者操作寫入Memstore的同時,也會寫一份資料到HLog檔案,HLog檔案定期會滾動出新,並刪除舊的檔案(已持久化到 StoreFile中的資料)。當HRegionServer意外終止後,HMaster會通過Zookeeper感知,HMaster首先處理遺留的 HLog檔案,將不同regionlog資料拆分,分別放到相應region目錄下,然後再將失效的region(帶有剛剛拆分的log重新分配,領取到這些region的 HRegionServerLoad Region的過程中,會發現有歷史HLog需要處理,因此會Replay HLog中的資料到MemStore中,然後flushStoreFiles完成資料恢復。

補充3Region就是StoreFilesStoreFiles裡由HFile構成,Hfile裡由hbasedata塊構成,一個data塊裡面又有很多keyvalue對,每個keyvalue裡存了我們需要的值。

補充4

Hbase寫資料,存資料,讀資料的詳細過程

我們觀察上面這一幅圖:

一 張表,有兩個列族(紅顏色的一個,黃顏色的一個),一個列族有兩個列,從圖中可以看出,這就是列式資料庫的最大特點,同一個列族的資料在在一起的,我們還 發現如果是有多個版本,同時也會存多個版本。最後我們還發現裡面存了這樣的值:r1:鍵值,cf1:列族的名字,c1:列明。t1:版本號,value值 (最後一幅圖說明的是value值可以存放的位置)。通過這樣的看法,我們發現如果我們設計表的時候把這幾個東西:r1:鍵值,cf1:列族的名 字,c1:列明的名字取短一點是不是我們會省出好多儲存的空間!

還有,我們從這一幅圖中還應該得到這樣的認識

我 們看倒數第二張圖,欄位篩選的效率從左到右明顯下降,所以在keyvalue的設計時使用者可以考慮把一些重要的篩選資訊左移到合適的位置,從而在不改變數 據量的情況下,提高查詢效能。那麼簡單的說就是使用者應當儘量把查詢維度或資訊儲存在行健中,因為它篩選資料的效率最高。

得到上面的認識後,我們應該還要會有這樣的覺悟:

HBase 的資料儲存時會被有順序的儲存到一個特定的範圍,因為我們儲存的時候一般都是按順序的,所以會一直存到同一個region上,由於一個region只能由 一個伺服器管理,這樣我們老是新增到同一個region上,會造成讀寫熱點,從而使叢集效能下降。那麼解決這個的辦法還是有的,我能想到的就是,比如我們 有9臺伺服器,那麼我們就回去當前時間,然後摸9,加到行健字首,這樣就會被平均的分到不同的region伺服器上了,這樣帶來的好處是,因為相連的資料 都分佈到不同的伺服器上了,使用者可以多執行緒並行的讀取資料,這樣查詢的吞吐量會提高。

關於我們版本的控制,我們要麼就讓多臺伺服器上的時間都同步,要麼乾脆就在put插入資料的時候,就設定一個客戶端的時間戳來代替。(因為我們要是不顯示的新增,人家就給我們在自己的伺服器上添加了自己的時間了。)

補充5:

設 計表的時候,有兩種設計方式,一種是高表設計,一種是胖表設計。根據HBase的拆分規則,我們的高表設計更容易拆分(使用組合鍵),不過,如果我們設計 成胖表,而我們的這個胖裡的資料需要經常修改,這樣設計是很合理的,因為我們的HBase保證了行級的原子性,如果設計成高表,反而就不合適了,因為不能 保證跨行的原子性。

補充6:

寫快取

每 一個put的操作實際上是RPC的操作,它將客戶端的資料傳送到伺服器然後返回,這隻適合小資料量的操作,如果有個應用程式需要每秒儲存上千行資料到 HBase表中,這樣處理就不太合適了。HBase的API配備了一個客戶端的寫緩衝區,緩衝區負責收集put操作,然後呼叫RPC操作一次性將put送 往伺服器。預設情況下,客戶端緩衝區是禁止的。可以通過自動刷寫設定為FALSE來啟用緩衝區。 table.setAutoFlush(false);void flushCommits () throws IOException這個方法是強制 將資料寫到伺服器。使用者還可以根據下面的方法來配置客戶端寫緩衝區的大小。 void setWritaeBufferSize(long writeBufferSize) throws IOException;預設大小是 2MB,這個也是適中的,一般使用者插入的資料不大,不過如果你插入的資料大的話,可能要考慮增大這個值。從而允許客戶端更高效地一定數量的資料組成一組通 過一次RPC請求來執行。給每個使用者的HTable設定一個寫緩衝區也是一件麻煩的事,為了避免麻煩,使用者可以在

Hbase-site.xml中給使用者設定一個較大的預設值。

<property>

<name>hbase.client.write.buffer</name>

<value>20971520</value>

</property>

補充7:

hbase支援大量的演算法,並且支援列族級別以上的壓縮演算法,除非有特殊原因,不然我們應該儘量使用壓縮,壓縮通常會帶來較好的 效能。通過一些測試,我們推薦使用SNAPPY這種演算法來進行我們hbase的壓縮。

Hbase讀資料:

client->zookeeper->.ROOT->.META-> 使用者資料表zookeeper記錄了.ROOT的路徑資訊(root只有一個region),.ROOT裡記錄了.META的region資訊, (.META可能有多個region),.META裡面記錄了region的資訊。

補充1:

在 HBase中,所有的儲存檔案都被劃分成了若干個小儲存塊,這些小儲存塊在get或scan操作時會載入到記憶體中,他們類似於RDBMS中的儲存單元頁。 這個引數的預設大小是64K。通過以上方式設定:void setBlocksize(int s);(HBase中Hfile的預設大小就是64K跟 HDFS的塊是64M沒關係)HBase順序地讀取一個數據塊到記憶體快取中,其讀取相鄰的資料時就可以再記憶體中讀取而不需要從磁碟中再次讀取,有效地減少 了磁碟I/O的次數。這個引數預設為TRUE,這意味著每次讀取的塊都會快取到記憶體中。但是,如果使用者順序讀取某個特定的列族,最好將這個屬性設定為 FALSE,從而禁止使用快取快。上面這樣描述的原因:如果我們訪問特定的列族,但是我們還是啟用了這個功能,這個時候我們的機制會把我們其它不需要的列 族的資料也載入到了記憶體中,增加了我們的負擔,我們使用的條件是,我們獲取相鄰資料。 void setBlockCacheEnabled(boolean blockCacheEnable);

補充2:

1:禁止自動刷寫。

我們有大批資料要插入時,如果我們沒有禁止,Put例項會被逐個的傳送到regio伺服器

,如果使用者禁止了自動刷寫的功能,put操作會在寫緩衝區被填滿時才會被送出。

2:使用掃描快取。

如果HBase被用作一個mapreduce作業的輸入源,請最好將作為mapreduce作業輸入掃描

器例項的快取用setCaching()方法設定為比預設值1更大的數。使用預設值意味著map

任務會在處理每條記錄時都請求region伺服器。不過,這個值要是500的話,則一次

可傳送500條資料到客戶端進行處理,當然了這資料也是根據你的情況定的。

這個是行級的,在我們的119頁有說明。

3:限定掃描範圍。

這個是很好理解的,比如我們要處理大量行(特別是作為mapreduce的輸入源),其中

用到scan的時候我們有Scan.addFamily();的方法,這個時候我們如果只是需要到

這個列族中的幾個列,那麼我們一定要精確。因為過多的列會導致效率的損失。

4:關閉resultScanner

當然了這個不能提高我們的效率,但是如果沒關就會對效率有影響。

5:塊快取的用法

首先我們的塊快取是通過Scan.setCacheBolcks();的啟動的,那些被頻繁訪問的行

我們應該使用快取塊,但是mapreduce作業使用掃描大量的行,我們就不該使用這個

了。(這個塊快取跟我在第四節中提到的那個塊是不一樣的)。

6:優化獲取行健的方式

當然用這個的前提是,我們只需要表中的行健時,才能用。那麼怎麼用在411頁有說明。

7:關閉Put上的WAL

書上是這麼說,但是我個人覺得這個功能還是不用的好,因為我們關閉了這個功能,

伺服器就不會把put寫入到WAL,而是直接寫到memstore裡,這樣一旦伺服器出現故障

我們的資料就丟失了。