Hbase基礎篇

動物園裡的一隻程式猿發表於2018-08-22

namespace:名稱空間的作用是把多個屬於相同業務領域的表分成一個組。一個表可以自由選擇是否有名稱空間,如果建立表的時候加上了名稱空間後,這個表名字就成為了:<NameSpace> : <Table>。通過名稱空間我們可以像關係型資料庫一樣將表分組,對於不同的組進行不同的環境設定,比如配額管理、安全管理

Hbase:系統表空間,用於HBase內部表。default:那些沒有定義表空間的表都被自動分配到這個表空間下

Table(表):一個表由一個或者多個列族組成。資料屬性,比如超時時間(TTL),壓縮演算法(COMPRESSION)等,都在列族的定義中定義。定義完列族後表是空的,只有新增了行,表才有資料

Row(行):一個行包含了多個列,這些列通過列族來分類。行中的資料所屬列族只能從該表所定義的列族中選取,不能定義這個表中不存在的列族,否則你會得到一個NoSuchColumnFamilyException。由於HBase是一個列式資料庫,所以一個行中的資料可以分佈在不同的伺服器上

Column Family(列簇):HBase會盡量把同一個列族的列放到同一個伺服器上,這樣可以提高存取效能,並且可以批量管理有關聯的一堆列。所有的資料屬性都是定義在列族上。

如下圖為表設定了兩個列族,而且,定義了每一個列族中要存放的列,如圖所示:{Name} -> Column Family-A, {City, Phone, Gender} -> Column Family-B,不同列族的資料會被儲存在不同的路徑中。即,設定多個列族時一行資料可能存在於兩個路徑中。整行讀取的時候,需要將兩個路徑中的資料合併在一起才可以獲取到完整的一行記錄。但如果僅僅讀取Name一列的話,只需要讀取Column Family-A即可

 

Column Qualifier(列):多個列組成一個行。列族和列經常用Column Family: Column Qualifier來一起表示。列是可以隨意定義的,一個行中的列不限名字、不限數量,只限定列族

一個列中可以儲存多個版本的資料。而每個版本就稱為一個單元格(Cell),所以在HBase中的單元格跟傳統關係型資料庫的單元格概念不一樣。HBase中的資料細粒度比傳
統資料結構更細一級,同一個位置的資料還細分成多個版本

Timestamp(時間戳/版本號):你既可以把它稱為是時間戳,也可以稱為是版本號,因為它是用來標定同一個列中多個單元格的版本號的。當你不指定版本號的時候,系統會自動採用當前的時間戳來作為版本號

rowkey的排序規則:每一個行都有一個類似主鍵的rowkey,而這個rowkey在HBase中是嚴格按照字典排序的,也就是說11比2小,row11會排在row1和row2中間

ACID就是Atomicity(原子性)、Consistency(一致性)、Isolation(隔離性)、Durability(永續性)的首字母縮寫。ACID是事務正確執行的保證。HBase部分支援了ACID

一、Hbase巨集觀架構

 

Master:負責啟動的時候分配Region到具體的RegionServer,執行各種管理操作,比如Region的分割和合並,建立表、修改列族配置

RegionServer:RegionServer上有一個或者多個Region。我們讀寫的資料就儲存在Region上。如果你的HBase是基於HDFS的,那麼Region所有資料存取操作都是呼叫了HDFS的客戶端介面來實現的

Region:表的一部分資料。HBase是一個會自動分片的資料庫。一個Region就相當於關係型資料庫中分割槽表的一個分割槽

HDFS:Hadoop的一部分。HBase並不直接跟伺服器的硬碟互動,而是跟HDFS互動,所以HDFS是真正承載資料的載體

ZooKeeper:把Master伺服器關掉你的業務系統照樣跑,能讀能寫;但是把ZooKeeper關掉,你就不能讀取資料了,因為你讀取資料所需要的後設資料表hbase:meata的位置儲存在ZooKeeper上

1、RegionServer架構

一個WAL:預寫日誌,WAL是Write-Ahead Log的縮寫。從名字就可以看出它的用途,就是:預先寫入。當操作到達Region的時候,HBase先不管三七二十一把操作寫到WAL裡面去。HBase會先把資料放到基於記憶體實現的Memstore裡,等資料達到一定的數量時才刷寫(flush)到最終儲存的HFile內。而如果在這個過程中伺服器當機或者斷電了,那麼資料就丟失了。WAL是一個保險機制,資料在寫到Memstore之前,先被寫到WAL了。這樣當故障恢復的時候可以從WAL中恢復資料

多個Region:Region相當於一個資料分片。每一個Region都有起始rowkey和結束rowkey,代表了它所儲存的row範圍

1.1、Region結構

多個Store:每一個Region內都包含有多個Store例項。一個Store對應一個列族的資料,如果一個表有兩個列族,那麼在一個Region裡面就有兩個Store。在最右邊的單個Store的解剖圖上,我們可以看到Store內部有MemStore和HFile這兩個組成部分。

1.1.1 Store 結構

  

MemStore:每個Store中有一個MemStore例項。資料寫入WAL之後就會被放入MemStore。MemStore是記憶體的儲存物件,只有當MemStore滿了的時候才會將資料刷寫(flush)到HFile中。

HFile:在Store中有多個HFile。當MemStore滿了之後HBase就會在HDFS上生成一個新的HFile,然後把MemStore中的內容寫到這個HFile中。HFile直接跟HDFS打交道,它是資料的儲存實體

  WAL是儲存在HDFS上的,Memstore是儲存在記憶體中的,HFile又是儲存在HDFS上的。資料是先寫入WAL,再被放入Memstore,最後被持久化到HFile中。
資料在進入HFile之前已經被儲存到HDFS一次了,為什麼還需要被放入Memstore?
  這是因為HDFS上的檔案只能建立、追加、刪除,但是不能修改。那就是使用記憶體先把資料整理成順序存放,然後再一起寫入硬碟。這就是Memstore存在的意義。Memstore存在的意義是維持資料按照rowkey順序排列,而不是做一個快取

1.1.2 MemStore

  資料被寫入WAL之後就會被載入到MemStore中去。MemStore的大小增加到超過一定閥值的時候就會被刷寫到HDFS上,以HFile的形式被持久化起來

  (1)由於HDFS上的檔案不可修改,為了讓資料順序儲存從而提高讀取效率,HBase使用了LSM樹結構來儲存資料。資料會先在Memstore中整理成LSM樹,最後再刷寫到HFile上。不過不要想當然地認為讀取也是先讀取Memstore再讀取磁碟喲!讀取的時候是有專門的快取叫BlockCache,這個BlockCache如果開啟了,就是先讀BlockCache,讀不到才是讀HFile+Memstore,

  (2)優化資料的儲存。比如一個資料新增後就馬上刪除了,這樣在刷寫的時候就可以直接不把這個資料寫到HDFS上

  每一次的刷寫都會產生一個全新的HFile檔案,由於HDFS的特性,所以這個檔案不可修改

1.1.3 HFile

  

  HFile是資料儲存的實際載體,我們建立的所有表、列等資料都儲存在HFile裡面  

  我們可以看到HFile是由一個一個的塊組成的。在HBase中一個塊的大小預設為64KB,由列族上的BLOCKSIZE屬性定義。
  Data:資料塊。每個HFile有多個Data塊。我們儲存在HBase表中的資料就在這裡。Data塊其實是可選的,但是幾乎很難看到不包含Data塊的HFile。
  Meta:後設資料塊。Meta塊是可選的,Meta塊只有在檔案關閉的時候才會寫入。Meta塊儲存了該HFile檔案的後設資料資訊,
  FileInfo:檔案資訊,其實也是一種資料儲存塊。FileInfo是HFile的必要組成部分,是必選的。它只有在檔案關閉的時候寫入,儲存的是這個檔案的資訊,比如最後一個Key(LastKey),平均的Key長度(Avg Key Len)等。
  DataIndex:儲存Data塊索引資訊的塊檔案。索引的資訊其實也就是Data塊的偏移值(offset)。DataIndex也是可選的,有Data塊才有DataIndex。
  MetaIndex:儲存Meta塊索引資訊的塊檔案。MetaIndex塊也是可選的,有Meta塊才有MetaIndex。
  Trailer:必選的,它儲存了FileInfo、DataIndex、MetaIndex塊的偏移值

   布隆過濾器:所有資料在Hbase中都以一個個HFile為單元來儲存,一個HFile大小概為幾百兆位元組到幾千兆位元組,每一個檢索請求到來的時候,掃描器都會從頭到尾地掃描整個HFile。為了提高HFile的檢索速度,HBase使用了塊索引機制。原理就是在HFile中增加一個部分,單獨儲存該HFile中的所有行鍵,這樣掃描器可以先通過檢索塊索引來查詢行鍵,當找到行鍵後再去具體的位置獲取該行的其他資訊。

  布隆過濾器可以知道元素在集合中是否“不存在”或者“可能存在”,也就是說如果布隆過濾器認為該元素不存在,那麼就是不存在。這樣可以極大地加速檢索的速度,因為當布隆過濾器認為要檢索的資料不在該塊索引中時,掃描器可以跳過那些絕對不需要掃描的塊索引

  

  

 

1.2 WAL預寫日誌

預寫日誌(Write-ahead log,WAL)就是設計來解決當機之後的操作恢復問題的。資料到達Region的時候是先寫入WAL,然後再被載入到Memstore的。就算Region的機器宕掉了,由於WAL的資料是儲存在HDFS上的,所以資料並不會丟失

延遲寫入WAL:當你的改動,比如Put、Delete、Append來到Region的時候會先放在記憶體中,這些改動立刻就會被寫入WAL。

Region會等到條件滿足的時候才把操作寫入WAL。這裡提到的條件主要指的是時間間隔hbase.regionserver.optionallogflushinterval,這個時間間隔的意思是HBase間隔多久會把操作從記憶體寫入WAL,預設值是1s

WAL滾動:WAL一定是一個環狀的滾動日誌結構,因為這種結構寫入效果最高,而且可以保證空間不會持續變大。

WAL滾動的條件是:

  1、WAL的檢查間隔由hbase.regionserver.logroll.period定義,預設值為1小時。檢查的內容是把當前WAL中的操作跟實際持久化到HDFS(HFile中)上的操作比較,看哪些操作已經被持久化了,被持久化的操作就會被移動到.oldlogs資料夾內(這個資料夾也是在HDFS上的)。

  2、當WAL檔案所在的塊(Block)快要滿了。

  3、當WAL所佔的空間大於或者等於某個閥值:你如果是基於HDFS的,預設WAL所佔塊空間大於或等於95%塊大小,這個WAL檔案就會被歸檔到.oldlogs資料夾內

刪除.oldlogs條件:  

  Master會負責定期地去清理.oldlogs資料夾,條件是“當這個WAL不需要作為用來恢復資料的備份”的時候。判斷的條件是“沒有任何引用指向這個WAL檔案。

  1、TTL程式:該程式會保證WAL檔案一直存活直到達到hbase.master.logcleaner.ttl定義的超時時間(預設10分鐘)為止

       2、備份(replication)機制:如果你開啟了HBase的備份機制,那麼HBase要保證備份叢集已經完全不需要這個WAL檔案了,才會刪除這個WAL檔案。

 

二、增刪改查

  1、當你新增一個單元格的時候,HBase在HDFS上新增一條資料。

  2、當你修改一個單元格的時候,HBase在HDFS又新增一條資料,只是版本號比之前那個大(或者你自己定義)。

  3、當你刪除一個單元格的時候,HBase還是新增一條資料!只是這條資料沒有value,型別為DELETE,這條資料叫墓碑標記(Tombstone)。

  真正的刪除資料

  由於資料庫在使用過程中積累了很多增刪查改操作,資料的連續性和順序性必然會被破壞。為了提升效能,HBase每間隔一段時間都會進行一次合併(Compaction),合併的物件為HFile檔案。合併分為兩種minor compaction和major compaction。

  在HBase進行major compaction的時候,它會把多個HFile合併成1個HFile,在這個過程中,一旦檢測到有被打上墓碑標記的記錄,在合併的過程中就忽略這條記錄。這樣在新產生的HFile中,就沒有這條記錄了,自然也就相當於被真正地刪除了

 

三、總結歸納

  

1、一個RegionServer包含多個Region,劃分規則是:一個表的一段鍵值在一個RegionServer上會產生一個Region。不過當你1行的資料量太大了(要非常大,否則預設都是不切分的),HBase也會把你的這個Region根據列族切分到不同的機器上去。

2、一個Region包含多個Store,劃分規則是:一個列族分為一個Store,如果一個表只有一個列族,那麼這個表在這個機器上的每一個Region裡面都只有一個Store。

3、一個Store裡面只有一個Memstore。

4、一個Store裡面有多個HFile(StoreFile是HFile的抽象物件,所以如果說到StoreFile就等於HFile)。每次Memstore的刷寫(flush)就產生一個新的HFile出來

 

寫入和讀出過程

1、寫入

WAL:資料被髮出之後第一時間被寫入WAL。由於WAL是基於HDFS來實現的,所以也可以說現在單元格就已經被持久化了,但是WAL只是一個暫存的日誌,它是不區分Store的。這些資料是不能被直接讀取和使用。
Memstore:資料隨後會立即被放入Memstore中進行整理。Memstore會負責按照LSM樹的結構來存放資料。這個過程就像我們在打牌的時候,抓牌之後在手上對牌進行整理的過程。
HFile:最後,當Memstore太大了達到尺寸上的閥值,或者達到了刷寫時間間隔閥值的時候,HBaes會被這個Memstore的內容刷寫到HDFS系統上,稱為一個儲存在硬碟上的HFile檔案。至此,我們可以稱為資料真正地被持久化到硬碟上,就算當機,斷電,資料也不會丟失了

2、讀出

讀取順序是先從BlockCache中找資料,找不到了再去Memstore和HFile中查詢資料

為了要過濾墓碑標記的資料,HBase的Scan操作在取到所需要的所有行鍵對應的資訊之後還會繼續掃描下去,直到被掃描的資料大於給出的限定條件為止,這樣它才能知道哪些資料應該被返回給使用者,而哪些應該被捨棄。所以你增加過濾條件也無法減少Scan遍歷的行數,只有縮小STARTROW和ENDROW之間的行鍵範圍才可以明顯地加快掃描的速度。在Scan掃描的時候store會建立StoreScanner例項。StoreScanner會把MemStore和HFile結合起來掃描。其中紅色塊部分都是屬於指定row的資料,Scan要把所有符合條件的StoreScanner都掃描過一遍之後才會返回資料給使用者

 

Region 定位

(1)客戶端先通過ZooKeeper的/hbase/meta-region-server節點查詢到哪臺RegionServer上有hbase:meta表。

(2)客戶端連線含有hbase:meta表的RegionServer。hbase:meta表儲存了所有Region的行鍵範圍資訊,通過這個表就可以查詢出你要存取的rowkey屬於哪個Region的範圍裡面,以及這個Region又是屬於哪個RegionServer。

(3)獲取這些資訊後,客戶端就可以直連其中一臺擁有你要存取的rowkey的RegionServer,並直接對其操作。

(4)客戶端會把meta資訊快取起來,下次操作就不需要進行以上載入hbase:meta的步驟了

  habse:meta.:是一張後設資料表,它儲存了所有Region的簡要資訊。.META.表中的一行記錄就是一個Region,該行記錄了該Region的起始行、結束行和該Region的連線資訊,這樣客戶端就可以通過這個來判斷需要的資料在哪個Region上。

 

四、rowkey設計

1、reversing

初步設計出的RowKey在資料分佈上不均勻,但RowKey尾部的資料卻呈現出了良好的隨機性,此時,可以考慮將RowKey的資訊翻轉,或者直接將尾部的bytes提前到RowKey的前部

缺點:場景利於Get但不利於Scan,因為資料在原RowKey上的自然順序已被打亂

2、salting

Salting的原理是在原RowKey的前面新增固定長度的隨機bytes,隨機bytes能保障資料在所有Regions間的負載均衡

缺點:既然是隨機bytes,基於原RowKey查詢時無法獲知隨機bytes資訊是什麼,也就需要去各個可能的Regions中去檢視。可見, Salting對於讀取是糟糕的

3、hashing

基於RowKey的完整或部分資料進行Hash,而後將Hashing後的值完整替換原RowKey或部分替換RowKey的字首部分

缺點:與Reversing類似, Hashing也不利於Scan,因為打亂了原RowKey的自然順序。

六、Hbase問題

1、Hbase多列簇

每個regionServer有多個region,每個region有多個store。每個store包含一個memstore和多個storeFile

在Hbase表中,每個列簇對應region中的一個store,region的大小達到閾值會分裂,因此如果表中有多個列簇的時候,可能會出現以下情況

(1) 一個region中有多個store:如果每個CF的資料分佈不均勻,比如CF1有100萬,CF2有1萬,則region分裂會導致CF2在每個region中資料量太少,查詢CF2時候會橫跨多個region導致效率變低

(2) 如果每個CF均勻分部:CF1為50萬,CF250萬,CF350萬,則region分裂時候,導致每個CF在region的資料量偏少,查詢某個CF時會導致很快多個Region的概率增大

(3)多個CF:有多個CF代表有多個store,也就是有多個memestore,也就是會導致記憶體的開銷增大,使得效率降低

(4)快取重新整理和壓縮:region中的快取重新整理和壓縮是基本操作,即一個CF出現快取重新整理或壓縮操作,其他CF也會做同一樣的操作,當列簇太多時就會導致IO頻繁