Hbase寫數據,存數據,讀數據的詳細過程


轉自:http://www.aboutyun.com/thread-10886-1-1.html

附HBase 0.94之后Split策略: http://www.aboutyun.com/thread-11211-1-1.html

 

1.Client寫入需要哪些過程?
2.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寫數據和存數據的過程

<ignore_js_op>  

 

Client寫入 -> 存入MemStore,一直到MemStore滿 -> Flush成一個StoreFile,直至增長到一定閾值 -> 出發Compact合並操作 -> 多個StoreFile合並成一個StoreFile,同時進行版本合並和數據刪除 -> 當StoreFiles Compact后,逐步形成越來越大的StoreFile -> 單個StoreFile大小超過一定閾值后,觸發Split操作,把當前Region Split成2個Region,Region會下線,新Split出的2個孩子Region會被HMaster分配到相應的HRegionServer 上,使得原先1個Region的壓力得以分流到2個Region上由此過程可知,HBase只是增加數據,有所得更新和刪除操作,都是在Compact階段做的,所以,用戶寫操作只需要進入到內存即可立即返回,從而保證I/O高性能。

對上述流程的補充:

補充1:HStore存儲是HBase存儲的核心,其中由兩部分組成,一部分是MemStore,一部分是StoreFiles。
補充2:HLog的功能:
在分布式系統環境中,無法避免系統出錯或者宕機,一旦HRegionServer以外退出,
MemStore中的內存數據就會丟失,引入HLog就是防止這種情況。
工作機制:每 個HRegionServer中都會有一個HLog對象,HLog是一個實現Write Ahead Log的類,每次用戶操作寫入Memstore的同時,也會寫一份數據到HLog文件,HLog文件定期會滾動出新,並刪除舊的文件(已持久化到 StoreFile中的數據)。當HRegionServer意外終止后,HMaster會通過Zookeeper感知,HMaster首先處理遺留的 HLog文件,將不同region的log數據拆分,分別放到相應region目錄下,然后再將失效的region(帶有剛剛拆分的log)重新分配,領取到這些region的 HRegionServer在Load Region的過程中,會發現有歷史HLog需要處理,因此會Replay HLog中的數據到MemStore中,然后flush到StoreFiles,完成數據恢復。
補充3:Region就是StoreFiles,StoreFiles里由HFile構成,Hfile里由hbase的data塊構成,一個data塊里面又有很多keyvalue對,每個keyvalue里存了我們需要的值。
補充4:
<ignore_js_op>  
我們觀察上面這一幅圖:
一 張表,有兩個列族(紅顏色的一個,黃顏色的一個),一個列族有兩個列,從圖中可以看出,這就是列式數據庫的最大特點,同一個列族的數據在在一起的,我們還 發現如果是有多個版本,同時也會存多個版本。最后我們還發現里面存了這樣的值: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中給用戶設置一個較大的預設值。
  1. <property>
  2. <name>hbase.client.write.buffer</name>
  3. <value>20971520</value>
  4. </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里,這樣一旦服務器出現故障
我們的數據就丟失了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM