HBase基礎架構解析
本文章來源於:https://github.com/Zeb-D/my-review ,請star 強力支援,你的支援,就是我的動力。
[TOC]
概述
HBase作為NoSQL的一員,在效能上不如memcached和redis,但是持久化儲存方面比記憶體NoSQL強。作為文件型NoSQL在分散式儲存上比mongo做sharding和MapReduce分析方便多。
HBase是一個分散式儲存、資料庫引擎,可以支援千萬的QPS、PB級別的儲存。
場景
HBase可以說是一個數據庫,也可以說是一個儲存。擁有雙重屬性的HBase天生就具備廣闊的應用場景。在最近的一些版本中,引入了OffHeap降低gc影響,優化鏈路延遲,提供Replica等可以滿足線上的需求。引入MOB,可以儲存10M左右的物件,完全適應了物件儲存。另外由於自身的併發能力、儲存能力,可以說是具有最為競爭力的引擎
- 物件儲存:我們知道不少的頭條類、新聞類的的新聞、網頁、圖片儲存在HBase之中,一些病毒公司的病毒庫也是儲存在HBase之中
- 時序資料:HBase之上有OpenTSDB模組,可以滿足時序類場景的需求
- 推薦畫像:特別是使用者的畫像,是一個比較大的稀疏矩陣,螞蟻的風控就是構建在HBase之上
- 時空資料:主要是軌跡、氣象網格之類,滴滴打車的軌跡資料主要存在HBase之中,另外在技術所有大一點的資料量的車聯網企業,資料都是存在HBase之中
- CubeDB OLAP:Kylin一個cube分析工具,底層的資料就是儲存在HBase之中,不少客戶自己基於離線計算構建cube儲存在hbase之中,滿足線上報表查詢的需求
- 訊息/訂單:在電信領域、銀行領域,不少的訂單查詢底層的儲存,另外不少通訊、訊息同步的應用構建在HBase之上
- Feeds流:典型的應用就是xx朋友圈類似的應用
- NewSQL:之上有Phoenix的外掛,可以滿足二級索引、SQL的需求,對接傳統資料需要SQL非事務的需求
更多的場景需要不斷挖掘
架構
邏輯儲存模型
HBase以表的形式儲存資料,表由行和列組成。列劃分為若干個列族,如下圖所示:
RowKey:Hbase使用Rowkey來唯一的區分某一行的資料。如圖中"rk001"
列族:Hbase通過列族劃分資料的儲存,列族下面可以包含任意多的列,實現靈活的資料存取。Hbase的列族不是越多越好,官方推薦的是列族最好小於或者等於3。我們使用的場景一般是1個列族。如圖中的“CF1”列族,下面包含兩個列:"Name"和"Alias"。
時間戳:TimeStamp對Hbase來說至關重要,因為它是實現Hbase多版本的關鍵。在Hbase中使用不同的timestame來標識相同rowkey行對應的不通版本的資料。
Cell:HBase 中通過 rowkey 和 columns 確定的為一個儲存單元稱為 cell。每個 cell 都儲存著同一份 資料的多個版本。版本通過時間戳來索引。
物理儲存模型
HBase的物理儲存模型如下圖:
在HBase中,資料以表的形式儲存,表由很多行組成,每一行由Row key(行鍵)以及一個或多個的列值組成。
當表有很多很多的Row時,我們把這個表按某些規則(比如每500條)拆分成很多部分,那麼拆分後的每一部分就是所謂的HRegion,這個HRegion作為一個整體被HMaster分配到某一個RegionServer中。
可以這樣理解,把HMaster想象成一個老大,他把HRegion給分配到某一個伺服器上,這樣一來,一個表就被分成多個HRegion並可能分配到了不同的RegionServer上。
我們剛說HRegion是一個整體,意思是他不能再繼續往下分割了,他必須在一個RegionServer上。
總體架構
HBase的總體結構圖如下:
包括了HMaster、HRegionSever、HRegion、HLog、Store、MemStore、StoreFile、HFile等。
HBase底層依賴HDFS,通過DFS Cilent進行HDFS操作。HMaster負責把HRegion分配給HRegionServer,每一個HRegionServer可以包含多個HRegion,多個HRegion共享HLog,HLog用來做災難恢復。
每一個HRegion由一個或多個Store組成,一個Store對應表的一個列族,每個Store中包含與其對應的MemStore以及一個或多個StoreFile(是實際資料儲存檔案HFile的輕量級封裝),MemStore是在記憶體中的,儲存了修改的資料,MemStore中的資料寫到檔案中就是StoreFile。
HMaster
HMaster的主要功能有:
- 把HRegion分配到某一個RegionServer。
- 有RegionServer宕機了,HMaster可以把這臺機器上的Region遷移到active的RegionServer上。
- 對HRegionServer進行負載均衡。
- 通過HDFS的dfs client介面回收垃圾檔案(無效日誌等)
注:HMaster沒有單點問題,HBase中可以啟動多個HMaster,通過Zookeeper的Master Election機制保證總有一個Master執行。
HRegionServer
- 維護HMaster分配給它的HRegion,處理對這些HRegion的IO請求,也就是說客戶端直接和HRegionServer打交道。(從圖中也能看出來)
- 負責切分正在執行過程中變得過大的HRegion
HRegion
每個HRegion由多個Store構成,每個Store儲存一個列族(Columns Family),表有幾個列族,則有幾個Store,每個Store由一個MemStore和多個StoreFile組成,MemStore是Store在記憶體中的內容,寫到檔案後就是StoreFile。StoreFile底層是以HFile的格式儲存。
HLog
HLog(WAL log):WAL意為write ahead log(預寫日誌),用來做災難恢復使用,HLog記錄資料的變更,包括序列號和實際資料,所以一旦region server 宕機,就可以從log中回滾還沒有持久化的資料。
HFile
HBase的資料最終是以HFile的形式儲存在HDFS中的,HBase中HFile有著自己的格式。
詳解
HFile
what
HFile是HBase中KeyValue資料的儲存格式(這裡不要把KeyValue想成Map的那種形式,理解起來會好一點),HFile是Hadoop的二進位制格式檔案,實際上StoreFile就是對HFile做了輕量級包裝,即StoreFile底層就是HFile 。
HFile由六部分組成:
-
Data(資料塊):儲存表中的資料(KeyValue的形式),這部分可以被壓縮。由多個block(塊)組成,每個塊的格式為:
[塊頭] + [key長] + [value長] + [key] + [value]。
-
Meta (元資料塊):儲存使用者自定義KeyValue。元資料是key-value型別的值,但元資料快只儲存元資料的value值,元資料的key值儲存在第五項(元資料索引塊)中。該塊由多個元資料值組成。
-
File Info:定長;記錄了檔案的一些元資訊,例如:AVG_KEY_LEN,AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。
fileInfo是以key值排序key-value型別的值。基本格式為: keyValue元素的個數 + (key + value型別id + value) + (key + value型別id + value) + ……
-
Data Index(資料塊索引):記錄了每個Data塊的起始索引。
該塊的組成為: 索引塊頭 + (資料塊在檔案中的偏移 + 資料塊長 + 資料塊的第一個key) + (資料塊在檔案中的偏移 + 資料塊長 + 資料塊的第一個key) + ……
-
Meta Index(元資料塊索引):記錄了每個Meta塊的起始索引。
該塊組成格式同資料塊索引,只是某部分的意義不一樣,組成格式: 索引塊頭 + (元資料在檔案中的偏移 + 元資料value長 + 元資料key) + (元資料在檔案中的偏移 + 元資料value長 + 元資料key) + ……
-
Trailer:定長;用於指向其他資料塊的起始點。
該塊記錄了其他各塊在hfile檔案中的偏移資訊和其他一些元資訊。組成格式如下: 檔案尾 + Fileinfo偏移 + 資料塊索引偏移 + 資料塊索引個數 + 元資料索引偏移 + 元資料索引個數 + 資料塊中未壓縮資料位元組數 + 資料塊中全部資料的key-value個數 + 壓縮程式碼標識 + 版本標識
我的理解 : Trailer中有指向其他資料塊的“指標”,通過Trailer能找到File Info,Data Index, Meta Index,然後通過Data Index和 Meta Index又能找到對應的資料塊和元資料塊。
同時這裡有幅圖不錯,建議認真看下這幅圖,很好懂:
Data結構
提到HFile中有Data(資料塊)部分,Data的結構又是如何的呢?
資料塊組成圖如下:
Data Block Magic{'D', 'A', 'T', 'A', 'B', 'L', 'K', 42 } | |||
---|---|---|---|
key長 | value長 | key | value |
key長 | value長 | key | value |
…… | |||
key長 | value長 | key | value |
Data Block Magic{'D', 'A', 'T', 'A', 'B', 'L', 'K', 42 } | |||
key長 | value長 | key | value |
key長 | value長 | key | value |
…… | |||
key長 | value長 | key | value |
Blocks …… |
資料塊部分由多個block塊組成,每個資料塊由塊頭 + 多個cell(key-value對)集合組成,如上圖。每個資料塊的大小在建立表的列族的時候可以指定,預設為(64 * 1024)。
- 塊大小的設定由HColumnDescriptor.setBlockSize(int)指定,預設(64 * 1024)。
- 塊大小設定,塊設定的越小,訪問速度越快,但資料塊索引越大,消耗的記憶體越多。因為在載入HFile時會把資料塊索引全部載入到記憶體中。
資料塊組成說明:
-
Data Block Magic – 資料塊頭,8位元組,固定位元組如下:{'D', 'A', 'T', 'A', 'B', 'L', 'K', 42 }。
-
key長 – 4位元組整型,記錄每個cell的key的長度。
-
value長 – 4位元組整型,記錄每個cell的value的長度。
-
key – cell的key值,byte[]型別,組成如下:
rowKey的長(2位元組)+ rowKey + family的長(1位元組) + family + qualify + timestampe(8位元組) + keyType型別(1位元組)**
1)rowKey的長度不能大於0x7fff(32767).
2)rowKey不能為空。
3)family(列族)的長度不能大於0x7f(127)
4)qualify(限定符)的長度不能大於(0x7fffffff(2147483647) – row長度 – family長度)。
-
value – cell的value值,byte[]型別,value值不能為空。
**例如:**在hbase中有一個表(student),其中有一個列族(info),該列族不壓縮。其中的rowkey用學號表示,現在插入一個記錄(rowkey='033', qualify='age', value='19')。那麼該記錄將被表示成一個cell(key-value對)儲存到HFile中,那麼該cell在HFile中的內容如下:
項 | 位元組表示 |
---|---|
key長 | {0,0,0,24} |
value長 | {0,0,0,2} |
key | {0,3}+{'0', '3', '3'}+{4}+{'i', 'n', 'f', 'o'}+{'a', 'g', 'e'}+{0,0,0,0,0,0,0,8}+{4} |
value | {'1', '9'} |
問題:
-
塊大小的設定策略?
-
keyType的說明?
-
compress壓縮的說明?
KeyValue結構
包含了:key length, value length, 緊接著是Key部分:開始是固定長度的數值,表示RowKey的長度,緊接著是 RowKey,然後是固定長度的數值,表示Family的長度,然後是Family,接著是Qualifier,然後是兩個固定長度的數值,表示Time Stamp和Key Type(Put/Delete)。Value部分沒有這麼複雜的結構,就是純粹的二進位制資料了。
舉個栗子: 執行這條語句:Put #1: rowkey=row1, cf:attr1=value1
。KeyValue的核心部分將會是這樣:
rowlength -----------→ 4(row1長度)
row -----------------→ row1
columnfamilylength --→ 2
columnfamily --------→ cf
columnqualifier -----→ attr1
timestamp -----------→ server time of Put
keytype -------------→ Put
元資料塊詳解
每個元資料是key-value型別的值,新增的元資料會按照從小到大的順序排序。
在StoreFile中,如果使用BloomFilter,則StoreFile將會把BloomFilter的資訊儲存到HFile中的元資料中, 元資料塊中只儲存元資料的value值,key值儲存在元資料索引塊中。格式如下:
Meta Block Magic{'M', 'E', 'T', 'A', 'B', 'L', 'K', 99 } |
---|
Meta Data Value |
Meta Block Magic{'M', 'E', 'T', 'A', 'B', 'L', 'K', 99 } |
Meta Data Value |
…… |
每個元資料由元資料頭+元資料值組成。
FileInfo詳解
fileInfo中儲存的資訊為key-value型別的值,其中key與value都是byte[]型別。每個新增的值在內部都以值key順序從小到大進行排序。fileInfo儲存了與該HFile相關的一些資訊,其中有系統保留的一些固定的值,這些值的key以”hfile.”為字首。也可以儲存使用者自定義的一些值,但這些值的key不能以”hfile.”開頭。其中系統內部保留的一些值如下:
項 | key(字串表示,實際以二進位制儲存) | value |
---|---|---|
LASTKEY | hfile.LASTKEY | 該HFile中的資料塊中的最後一個值的key, 該值如果為空則不進行儲存 |
AVG_KEY_LEN | hfile.AVG_KEY_LEN | 該HFile中的資料塊中的所有值key的平均長度。 |
AVG_VALUE_LEN | hfile.AVG_VALUE_LEN | 該HFile中的資料塊中的所有值value的平均長度。 |
COMPARATOR | hfile.COMPARATOR | 在HFile中的資料塊中的值都是以值的key進行排序來存放的,而key的組成比較複雜,這就需要一個key的比較器類,而該值儲存了key值比較器的類的名稱。 |
fileInfo在HFile中的格式如下:
filInfo中所有值(key-value對)的個數,整型 | ||
---|---|---|
key | value型別標識 | value |
key | value型別標識 | value |
…… |
fileInfo各項說明:
-
filInfo中所有值(key-value對)的個數,整型,四位元組。
-
key值,儲存fileInfo中值得key值。在HFile中的組成為
key長+key
其中key長以壓縮的整型儲存,整型型別包括(byte,short,int,long),如果該整數用i表示,詳細說明如下:
1. 當-112 <= i <= 127 時,用一個位元組儲存實際值。
2. 其他情況下,第一個位元組表示該整型與正負與該整數佔位元組長度,隨後儲存的是從該整數補碼的高位算起的第一個非0位元組的所有值。如果第一個位元組為v,詳細說明如下:
a) 當-120<=i<=-113時,表示該值為正數,該數所佔位元組為-(v+112)
b) 當-128<=i<=-121時,表示該值為負數,該數所佔位元組為-(v+120)
例如:
原始值 | 壓縮後,以位元組表示 | 說明 |
---|---|---|
-87 | {-87} | 第一種情況 |
127 | {127} | 第一種情況 |
-1246 | {-122} + {4, -35} | 第二種情況的b型別。{-122}表示該數為負數,並且所佔位元組長度為-(-122+120)=2位元組。其中{4,-35}儲存的是-1246的補碼1245的第一個非0位元組開始的所有位元組。1245的16進製為0x04DD,非0位元組共2個,第一個為0x04(4),第二個為0xDD(-35),組合一起為{-122, 4,-35} |
130 | {-113} + {-126} | 第二種情況的a型別。{-113}表示該數為正數,並且所佔位元組長度為-(-113+112)=1位元組。其中{-126}儲存的是130的補碼130的第一個非0位元組開始的所有位元組。130的16進製為0x04DD,非0位元組共2個,第一個為0x04(4),第二個為0x82(-126),組合一起為{-113, -126} |
- value值,儲存fileInfo中值的value值。在HFile中的組成為
value長+value
其中value長以壓縮的整型儲存,壓縮整型具體格式參考key值中關於壓縮整型的說明。
資料塊索引
資料塊索引儲存的是每一個數據塊在HFile檔案中的位置、大小資訊以及每個塊的第一個cell的key值。格式如下:
Index Block Magic{'I', 'D', 'X', 'B', 'L', 'K', 41, 43 } | ||
---|---|---|
block offset | block size | block first key |
block offset | block size | block first key |
…… | ||
block offset | block size | block first key |
格式各項說明:
-
block offset 塊在HFile中偏移,long(8位元組)。
-
block size 塊大小,int(4位元組)。
-
block first key 塊中第一個cell(key-value)值得key.該值的組成為(key的長(壓縮整型表示)+key值)
元資料塊索引
該資料塊的格式與資料庫索引相同,元資料塊索引儲存的是每一個元資料在HFile檔案中的位置、大小資訊以及每個元資料的key值。格式如下:
Index Block Magic{'I', 'D', 'X', 'B', 'L', 'K', 41, 43 } | ||
---|---|---|
meta offset | meta size | meta name |
meta offset | meta size | meta name |
…… | ||
meta offset | meta size | meta name |
格式各項說明:
-
meta offset 元資訊在HFile中偏移,long(8位元組)。
-
meta size 元資訊資料大小,int(4位元組)。
-
meta name 元資訊中的key值.該值的組成為(key的長(壓縮整型表示)+key值)
檔案尾
檔案尾主要儲存了該HFile的一些基本資訊。格式比較簡單,如下:
Trailer Block Magic{'T', 'R', 'A', 'B', 'L', 'K', 34, 36 } |
---|
FileInfo Offset (long) |
Data Index Offset (long) |
Data Index Count (int) |
Meta Index Offset (long) |
Meta Index Count (int) |
Total Uncompressed Bytes (long) |
Entry Count (int) |
Compression Codec (int) |
Version (int) |
說明如下:
-
FileInfo Offset – FileInfo資訊在HFile中的偏移。long(8位元組)。
-
DataIndex Offset – 資料塊索引在HFile中的偏移。long(8位元組)。
-
DataIndex Count – 資料塊索引的個數。int(4位元組)。
-
MetaIndex Offset – 元資料索引塊在HFile中的偏移。long(8位元組)。
-
MetaIndex Count – 元資料索引塊的個數。int(4位元組)。
-
TotalUncompressedBytes – 未壓縮的資料塊部分的總大小。long(8位元組)。
-
Entry Count – 資料塊中所有cell(key-value)的個數。int(4位元組)
-
Compression Codec – 壓縮演算法為enum型別,該值表示壓縮演算法程式碼。(LZO-0,GZ-1,NONE-2),int(4位元組)
-
Version – 版本資訊。當前該版本值為1. int(4位元組)。
HLog
HLog是用來做災難恢復的,為什麼這麼說呢?假設沒有HLog,我們進行一個寫請求,會首先寫到MemStore上,等到Memstore到達一定容量後,才會flush到storefile中。但是如果在這之前主機斷電了呢?那這部分操作的資料全丟失了。這顯然不是我們想到的結果,於是有了HLog,當發起一個寫請求時,會先往HLog中寫再往MemStore中寫,成功後(此時還沒被存到sotrefile)就給客戶端一個寫入成功的response。
WAL最重要的作用是災難恢復。和MySQL 的BIN log類似,它記錄所有的資料改動。一旦伺服器崩潰,通過重放log,我們可以恢復崩潰之前的資料。這也意味如果寫入WAL失敗,整個操作將認為失敗。
首先,客戶端初始化一個可能對資料改動的操作,如put(Put),delete(Delete) 和 incrementColumnValue()。這些操作都將被封裝在一個KeyValue物件例項中,通過RPC 呼叫傳送給HRegionServer(最好是批量操作)。 一旦達到一定大小,HRegionServer 將其傳送給HRegion。這個過程中,資料會首先會被寫入WAL,之後將被寫到實際存放資料的MemStore中。
當MemStore到達一定大小,或者經過一段時間後,資料將被非同步地寫入檔案系統中。然而,在兩次寫入檔案系統之間的資料,是保留在記憶體中的。如果這個時候系統崩潰,那資料···,別急,我們有WAL!
簡單總結:
HLog檔案就是一個普通的Hadoop Sequence File(也是KeyValue形式,與前面的資料塊中的KeyValue類比),
Sequence File 的Key是HLogKey物件,HLogKey中記錄了寫入資料的歸屬資訊,除了table和region名字外,
同時還包括 sequence number和timestamp,timestamp是“寫入時間”,sequence number的起始值為0,或者是最近一次存入檔案系統中sequence number。
HLog Sequece File的Value是HBase的KeyValue物件,即對應HFile中的KeyValue。包括:row,column family, qualifier, timestamp, value,以及“Key Type”(比如PUT或DELETE)
重要的類
HLog
HLog是實現WAL的類。一個HRegionServer對應一個HLog例項。當HRegion初始化時,HLog將作為一個引數傳給HRegion的建構函式。
HLog最核心的是呼叫doWrite的append() 方法,前面提到的可能對資料改動的操作都就將首先呼叫這個方法。出於效能的考慮,put(), delete() 和incrementColumnValue()有一個開關函式setWriteToWAL(boolean) , 設為false將禁用WAL。這是為什麼上圖中向下的箭頭是虛線的原因。預設時候當然需要WAL,但是假如你執行一個數據匯入的MapReduce Job,你可以通過關閉WAL獲得性能上的提升。
另一個重要的特性是HLog將通過“sequence number”追蹤資料改變。它內部使用AtomicLong保證執行緒安全。sequence number的起始值為0,或者是最近一次存入檔案系統中sequence number。Region開啟儲存檔案,讀取每個HFile中的最大的sequence number,如果該值大於HLog 的sequence number, 就將它作為HLog 的sequence number的值。最後,HLog將得到上次存入檔案和繼續記log的點。過會,我們將看到它的應用。
上圖表示了3個不同的region,每一個負責一段rowkey的範圍。這些region將共享同一個HLog例項,我們可以看出,從不同region來的資料寫入WAL的順序是不確定的。在後面我們會再詳細的介紹。
最後,Hlog利用HMaster恢復和切分一個由一個崩潰的HRegionServery遺留下來的Log。之後,重新部署regions。
HLogKey
WAL使用Hadoop的SequenceFile,它將記錄儲存為key/values 的資料集。對於WAL,key是一個HLogKey的例項。 KeyValue不僅包括row,column family, qualifier, timestamp, value, 還包括“Key Type”—派上用場啦, 這裡,可以用Key Type代表一個“put”或“delete”操作。
但是,哪裡去存放KeyValue的歸屬資訊,比如region或者表名呢?這些存放在HLogKey中。同時還包括 sequence number,和“寫入時間”, 是一個記錄資料何時寫入到log的時間戳。
LogFlusher
前面提到,資料以KeyValue形式到達HRegionServer,將寫入WAL,之後,寫入一個SequenceFile。看過去沒問題,但是因為資料流在寫入檔案系統時,經常會快取以提高效能。這樣,有些本以為在日誌檔案中的資料實際在記憶體中。這裡,我們提供了一個LogFlusher的類。它呼叫HLog.optionalSync(),後者根據“hbase.regionserver.optionallogflushinterval”(預設是10秒),定期呼叫Hlog.sync()。另外,HLog.doWrite()也會根據“hbase.regionserver.flushlogentries”(預設100秒)定期呼叫Hlog.sync()。Sync() 本身呼叫HLog.Writer.sync(),它由SequenceFileLogWriter實現。
LogRoller
Log的大小通過$HBASE_HOME/conf/hbase-site.xml 的“hbase.regionserver.logroll.period”限制,預設是一個小時。所以每60分鐘,會開啟一個新的log檔案。久而久之,會有一大堆的檔案需要維護。首先,LogRoller呼叫HLog.rollWriter(),定時滾動日誌,之後,利用HLog.cleanOldLogs()可以清除舊的日誌。它首先取得儲存檔案中的最大的sequence number,之後檢查是否存在一個log所有的條目的“sequence number”均低於這個值,如果存在,將刪除這個log。
這裡解釋下你可能在log中看到的令人費解的內容:
2009-12-15 01:45:48,427 INFO org.apache.hadoop.hbase.regionserver.HLog: Too` `many hlogs: logs=130, maxlogs=96; forcing flush of region with oldest edits:` `foobar,1b2dc5f3b5d4,1260083783909
這裡,我們看到,log file的數目超過了log files的最大值。這時,會強制呼叫flush out 以減少log的數目。
“hbase.regionserver.hlog.blocksize”和“hbase.regionserver.logroll.multiplier”兩個引數預設將在log大小為SequenceFile(預設為64MB)的95%時回滾。所以,log的大小和log使用的時間都會導致回滾,以先到達哪個限定為準
Replay
當HRegionServer啟動,開啟所管轄的region,它將檢查是否存在剩餘的log檔案,如果存在,將呼叫Store.doReconstructionLog()。重放一個日誌只是簡單地讀入一個日誌,將日誌中的條目加入到Memstore中。最後,flush操作將Memstore中資料flush到硬碟中。
舊日誌往往由region server 崩潰所產生。當HMaster啟動或者檢測到region server 崩潰,它將日誌檔案拆分為多份檔案,將其儲存在region所屬的資料夾。之後,根據上面提到的方法,將日誌重放。需要指出的是,崩潰的伺服器中的region只有在日誌被拆分和拷貝之後才能被重新分配。拆分日誌利用HLog.splitLog()。舊日誌被讀入主執行緒記憶體中,之後,利用執行緒池將其寫入所有的region資料夾中,一個執行緒對應於一個region。
問題
- 為什麼要一個RegionServer 對應於一個HLog。為什麼不是一個region對應於一個log file?
引用BigTable中的一段話,
如果我們每一個“tablet”(對應於HBase的region)都提交一個日誌檔案,會需要併發寫入大量的檔案到GFS,這樣,根據每個GFS server所依賴的檔案系統,寫入不同的日誌檔案會造成大量的磁碟操作。
HBase依照這樣的原則。在日誌被回滾和安全刪除之前,將會有大量的檔案。如果改成一個region對應於一個檔案,將會不好擴充套件,遲早會引發問題。
-
潛在問題
-
當server崩潰,HBase需要將其log切分成合適的片。然而,由於所有的條目混雜在日誌中,HMaster只有在將log完全分配到每一個server後,才能將崩潰server中的region重新分配。這個時間可能很長。
-
資料安全。你希望能儲存你所有的資料,雖然你能將flush的時間調到儘可能的低,你依然依賴於上面提到的檔案系統。那些用於儲存資料依舊有可能沒寫到磁碟而發生資料丟失。
-
很明顯,需要log來保證資料安全。最好是能讓一個日誌保持1個小時(或長)的開啟狀態。當資料來時,將新的key/value對寫入SequenceFile中,並定期flush資料到磁碟中。但是Hadoop不是這樣工作的。他提供了一個API,允許開啟一個檔案,寫入大量的資料,然後馬上關閉檔案,成為一個對其他人只讀的檔案。只有當檔案關閉時才是對其他人可讀的。那麼,如果一個程序在寫入檔案時僵死,那麼,資料很可能會丟失。因此,我們需要一個功能,能取到一個離崩潰伺服器寫入資料儘可能近的點。
插曲: HDFS append,hflush,hsync,sync ...
這些都起源於HADOOP-1700。Hadoop 0.19沒有能解決這個問題。這個問題後來又成為HADOOP-4379或HDFS-200,並實現了syncFS(),後者可以同步檔案的改變。同時,HBase-1470中,將這個API開放,但是依然沒有解決這個問題。
之後是HDFS-265,重新提出append的方案,並引入Syncable的介面,開放hsync()和hflush()。
SequenceFile.Writer.sync()和上面的並不相同。 它只是將一個同步標記寫入檔案,以方便資料恢復。
雖然append對於HDFS很有用,但並沒有用在HBase中。HBase用了hflush,它可以在log寫完成後將所有資料寫入磁碟。當伺服器崩潰,我們可以安全地將“髒”檔案讀到最後一次改動。Hadoop 0.19.0中,利用Hadoop fsck /可以根據HBase開啟的日誌檔案數目報告DFS的破損程度。
結論是,在Hadoop 0.21.0之前,你非常容易遇到資料丟失。在Hadoop 0.21.0之後,你將得到頂尖的系統。