天天看點

Hbase Architecture[下]1. Read Path2. Region查找3. ZooKeeper4. Replication

概述:

  1. Read Path
  2. Region查找

    2.1 Region生命周期

  3. ZooKeeper
  4. Replication

    4.1 Life of a Log Edit

    4.1.1 正常處理流程

    4.1.2 Non-responding Slave Clusters

    4.2 Internals

    4.2.1 選擇複制到目标Region Servers

    4.2.2 日志追蹤

    4.2.3 讀、過濾及發送Edits

    4.2.4 日志清理

    4.2.5 Region Server 故障恢複

    原文連結

1. Read Path

HBase中的每個column family可能有多個檔案,檔案中包含實際的cells或者是KeyValue執行個體。當memstore中積累的更新被flush到磁盤上時這些檔案就會建立出來。負責compaction的背景線程會通過将小檔案合并成更大的檔案來将檔案數控制在一定水準上。Major compaction最終會将所有的檔案集合壓縮成一個,之後随着flush的進行,小檔案又會出現。

因為所有的存儲檔案都是不可變的,是以就沒法直接将一個值從它們裡面删除,也沒法對某個值進行覆寫。而隻能通過寫入一個墓碑式的标記,來代表某個cell或者某幾個cell或者是整行都被删除了。

假設今天你在給定的一行裡寫了一個列,之後你一直不斷的添加資料,那麼你可能會為該行寫入另一個列。問題是,假設最初的列值已經被持久化到了磁盤中,而新寫入的列還在memstore中,或者已經被flush到了磁盤,那該行到底算存放到哪呢?換句話說,當你對該行執行一個get指令時,系統怎麼知道該傳回什麼内容?作為一個用戶端,你可能希望傳回所有的列—看起來它們好像就是一個實體一樣。但是實際的資料是存儲在獨立的KeyValue執行個體中的,而且可能跨越任意數目的存儲檔案。

如果你删除了最初的那個列值,然後再執行get操作,你希望該值已經不存在了,雖然實際上它還存在于某處,但是墓碑式的标記表明你已經把它删除了。但是該标記很有可能與你要删除的值是分開存儲的。關于該架構更細節的内容參見the section called “Seek vs. Transfer”。

該問題是通過使用QueryMatcher以及一個ColumnTracker解決的。在讀取所有的存儲檔案以找到一個比對的記錄之前,可能會有一個快速的排除檢查,可以使用時間戳或者Bloom filter來跳過那些肯定不包含該記錄的存儲檔案。然後,對剩餘的存儲檔案進行掃描以找到比對該key的記錄。

為何Gets即Scans

在HBase之前的版本中,Get方法的确是單獨實作的。最近的版本進行了改變,目前它内部已經和Scan API使用相同的源代碼。

你可能會很奇怪,按理來說一個簡單的Get應該比Scan快的。把它們區分對待,更容易針對Get進行某些優化。實際上這是由HBase本身架構導緻的,内部沒有任何的索引檔案來支援對于某個特定的行或列的直接通路。最小的通路單元就是HFile中的一個block,為了找到被請求的資料,RegionServer代碼和它的底層Store執行個體必須load那些可能包含該資料的blocks然後進行掃描。實際上這就是Scan的操作過程。換句話說,Get本質上就是對單個行的Scan,就是一個從start row到start row+1的scan。

Scan是通過RegionScanner類實作的,它會每個Store執行個體(每個代表一個column family)執行StoreScanner檢索,如果讀操作沒有包含某個column family,那麼它的Store執行個體就會被略過。

StoreScanner會合并它所包含的存儲檔案和memstore。同時這也是根據Bloomfilter或者時間戳進行排除性檢查的時候,然後你可以跳過那些不需要的存儲檔案。參見the section called “Key Design”了解排除性檢查的細節,以及如何利用它。

同時也是由StoreScanner持有QueryMatcher(這裡是ScanQueryMatcher類)。它會記錄下那些包含在最終結果中的KeyValue。

RegionScanner内部會使用一個KeyValueHeap類來按照時間戳順序安排所有的Store scanners。StoreScanner也會采用相同的方式來對存儲檔案進行排序。這就保證了使用者可以按照正确的順序進行KeyValue的讀取(比如根據時間戳的降序)。

在store scanners被打開時,它們會将自己定位到請求的row key處。準備進行資料讀取。

Figure 8.10. Rows are stored and scanned across different stores, on-disk or in-memory

HBase Architecture(譯):下 - 星星 - 銀河裡的星星

對于一個get()調用,所有的伺服器需要做的就是調用RegionScanner的next()。該調用内部會讀取組成結果的所有内容。包括所有請求的版本,假設某列有三個版本,同時使用者請求檢索它們中所有的。這三個KeyValue可能分布在磁盤或記憶體中的存儲檔案。Next()調用會從所有的存儲檔案中讀取直到讀到下一行,或者直到讀到足夠的版本。

與此同時,它也會記錄那些删除标記。當它掃描目前行的KeyValue時,可能會碰到這些删除标記,那些時間戳小于等于該删除标記的記錄都會被認為是已經清除掉了。

圖中展示了一個由一系列KeyValue組成的邏輯行,某些存儲在相同的存儲檔案中,某些在其他檔案上,包含了多個column family。由于時間戳或者Bloom filter的排除過程,某些存儲檔案和memstore可能會被跳過。最後一個存儲檔案中的删除标記可能會遮蔽掉所有的記錄,但是它們仍然是同一行的一部分。這些scanners—實際上可以用一系列指向存儲檔案的箭頭表示—要麼指向檔案中的第一個比對點,要麼是緊挨着所請求的key的那個點(如果沒有直接比對的點的話)。

在執行next調用時,隻有那些具有比對點的scanners才會被考慮。内部循環會從第一個存儲檔案到最後一個存儲檔案,按照時間地降序一個挨一個地讀取其中的KeyValue,直到超出目前請求的key。

對于scan操作,則是通過在ResultScanner上不斷的調用next(),直到碰到表的結束行或者為目前的batch讀取了足夠多的行時。

最終的結果是一個比對了給定的get或者scan操作的KeyValue的清單。它會被發送給用戶端,用戶端就可以使用API函數來通路裡面的列。

2. Region查找

為了讓用戶端能夠找到持有特定的row key range的region server,HBase提供了兩個特殊的中繼資料表:-ROOT-和.META.。

-ROOT-表用于儲存.META.表的所有regions的資訊。HBase認為隻有一個root region,同時它永不會被split,這樣就可以保證一個三層的類B+樹查找模式:第一層是存儲在ZooKeeper上的一個儲存了root 表的region資訊的節點,換句話說就是儲存了root region的那個region server的名稱。第二層需要到-ROOT-表中查找比對的meta region,然後第三層就是到.META.表中檢索使用者表的region資訊。

中繼資料表中的row key由每個region的表名,起始行,及一個ID(通常使用目前時間,機關是毫秒)。從HBase 0.90.0開始,這些key可能會有一個額外的與之關聯的hash值。目前隻是用于使用者表中。

注:Bigtable論文指出,在.META.表的region大小限制在128MB的情況下,它可以尋址2^34個regions,如果按每個region 128MB大小算,就是2^61位元組大小。因為region大小可以增加而不會影響到定位模式,是以根據需要這個值還可以增大。

盡管用戶端會緩存region位置資訊,但是用戶端在首次查詢時都需要發送請求來查找特定row key或者一個region也可能會被split,merge或者移動,這樣cache可能會無效。用戶端庫采用一種遞歸的方式逐層向上地找到目前的資訊。它會詢問與給定的row key比對的.META.表region所屬的region server位址。如果資訊是無效的,它就退回到上層詢問root表對應的.META. region的位置。最後,如果也失敗了,它就需要讀取Zookeeper節點以找到root表region的位置。

最壞情況下,将會需要6次網絡傳輸才能找到使用者region,因為無效記錄隻有當查找失敗時才能發現出來,當然系統假設這種情況并不經常發生。在緩沖為空的情況下,用戶端需要三次網絡傳輸來完成緩存更新。一種降低這種網絡傳輸次數的方法是對位置資訊進行預取,提前更新用戶端緩存。具體細節見the section called “Miscellaneous Features”。

Figure 8.11. Starting with an empty cache, the client has to do three lookups.

HBase Architecture(譯):下 - 星星 - 銀河裡的星星

一旦使用者表region已知之後,用戶端就可以直接通路而不需要進一步的查找。圖中對查找進行了标号,同時假設緩存是空的。

2.1. Region生命周期

Region的狀态會被master追蹤,通過使用AssignmentManager類。它會記下region從offline狀态開始的整個生命周期。表8.1列出了一個region的所有可能狀态。

Table 8.1. Possible states of a region

State

Description

Offline

The region is offline.

Pending Open

A request to open the region was sent to the server.

Opening

The server has started opening the region.

Open

The region is open and fully operational.

Pending Close

A request to close the region has been sent to the server.

Closing

The server is in the process of closing the region.

Closed

The region is closed.

Splitting

The server started splitting the region.

Split

The region has been split by the server.

狀态間的轉換可能是由master引起,也可能是由持有它的那個region server引起。比如master可能将region配置設定給某個server,之後它會由該server打開。另一方面,region server可能會啟動split過程,這會觸發region打開和關閉事件。

由于這些事件本身的分布式屬性,伺服器使用ZooKeeper在一個專門的znode中記錄各種狀态。

3. ZooKeeper

從0.20.x開始,HBase使用ZooKeeper作為它的分布式協調服務。包括region servers的追蹤,root region的位置及其他一些方面。0.90.x版引入了新的master實作,與ZooKeeper有了更緊密的內建。它使得HBase可以去除掉master和region servers之間發送的心跳資訊。這些現在都通過ZooKeeper完成了,當其中的某一部分發生變化時就會進行通知,而之前是通過固定的周期性檢查完成。

HBase會在它的根節點下建立一系列的znodes。根節點預設是”/hbase”,可以通過zookeeper.znode.parent進行配置。下面是所包含的znodes節點清單及其功用:

注:下面的例子使用了ZooKeeper指令行接口(簡稱CLI)來運作這些指令。可以通過如下指令啟動CLI:

ZK_HOME/bin/zkCli.sh -server

/hbase/hbaseid

包含了叢集ID,跟存儲在HDFS上的

hbase.id

檔案中的一緻. 如下:

[zk: localhost(CONNECTED) 1]

get /hbase/hbaseid

e627e130-0ae2-448d-8bb5-117a8af06e97

/hbase/master

包含了伺服器名稱, (具體參見the section called “Cluster Status Information” ). 如下:

[zk: localhost(CONNECTED) 2]

get /hbase/master

foo.internal,60000,1309859972983

/hbase/replication

包含了replication的細節資訊。相關細節參見 the section called “Internals”。

/hbase/root-region-server

包含了持有

-ROOT-

regions 的region server的伺服器名稱。在region查找過程中會用到它 (見 the section called “Region Lookups”). 如下:

[zk: localhost(CONNECTED) 3]

get /hbase/root-region-server

rs1.internal,60000,1309859972983

/hbase/rs

作為所有region servers的根節點,會記錄它們是何時啟動。用來追蹤伺服器的失敗。每個内部的znode節點是臨時性的,以它所代表的region server的伺服器名稱為名。比如:

[zk: localhost(CONNECTED) 4]

ls /hbase/rs[rs1.internal,60000,1309859972983,rs2.internal,60000,1309859345233]

/hbase/shutdown

該節點用于追蹤叢集狀态。包含叢集啟動時間,當叢集關閉時其内容為空。比如:

[zk: localhost(CONNECTED) 5]

get /hbase/shutdown Tue Jul 05 11:59:33 CEST 2011

/hbase/splitlog

用于所有log splitting相關協調的parent znode, 細節詳見 the section called “Log Splitting” 。比如:

[zk: localhost(CONNECTED) 6]

ls /hbase/splitlog

[hdfs%3A%2F%2Flocalhost%2Fhbase%2F.logs%2Ffoo.internal%2C60020%2C \1309850971208%2Ffoo.internal%252C60020%252C1309850971208.1309851636647,hdfs%3A%2F%2Flocalhost%2Fhbase%2F.logs%2Ffoo.internal%2C60020%2C \1309850971208%2Ffoo.internal%252C60020%252C1309850971208.1309851641956,…hdfs%3A%2F%2Flocalhost%2Fhbase%2F.logs%2Ffoo.internal%2C60020%2C \1309850971208%2Ffoo.internal%252C60020%252C1309850971208.1309851784396]

[zk: localhost(CONNECTED) 7]

get /hbase/splitlog/ \

\hdfs%3A%2F%2Flocalhost%2Fhbase%2F.logs%2Fmemcache1.internal%2C \

60020%2C1309850971208%2Fmemcache1.internal%252C60020%252C1309850971208. \

1309851784396 unassigned foo.internal,60000,1309851879862

[zk: localhost(CONNECTED) 8]

get /hbase/splitlog/ \

\hdfs%3A%2F%2Flocalhost%2Fhbase%2F.logs%2Fmemcache1.internal%2C \

60020%2C1309850971208%2Fmemcache1.internal%252C60020%252C1309850971208. \

1309851784396

owned foo.internal,60000,1309851879862

[zk: localhost(CONNECTED) 9]

ls /hbase/splitlog

[RESCAN0000293834, hdfs%3A%2F%2Flocalhost%2Fhbase%2F.logs%2Fmemcache1. \internal%2C60020%2C1309850971208%2Fmemcache1.internal%252C \60020%252C1309850971208.1309851681118, RESCAN0000293827, RESCAN0000293828, \RESCAN0000293829, RESCAN0000293838, RESCAN0000293837]

這些例子列出了很多東西:你可以看到一個未被配置設定的log是如何被split的,之後又如何被一個region server所擁有。”RESCAN”節點表示那些workers,比如萬一log split失敗後可能被用于進一步的工作的region server。

/hbase/table

當一個表被禁用時,它會被添加到該節點下. 表名就是新建立的znode的名稱,内容就是”DISABLED”。比如:

[zk: localhost(CONNECTED) 10]

ls /hbase/table

[testtable]

[zk: localhost(CONNECTED) 11]

get /hbase/table/testtable

DISABLED

/hbase/unassigned

該znode是由 AssignmentManager 用來追蹤整個叢集的region狀态的。它包含了那些未被打開或者處于過渡狀态的regions對應的znodes,zodes的名稱就是該region的hash。比如:

[zk: localhost(CONNECTED) 11]

ls /hbase/unassigned

[8438203023b8cbba347eb6fc118312a7]

4. Replication

HBase replication是在不同的HBase部署之間拷貝資料的一種方式。它可以作為一種災難恢複解決方案,也可以用于提供HBase層的更高的可用性。同時它也能提供一些更實用的東西:比如,可以作為從面向web的叢集中拷貝最新的更新内容到MapReduce叢集的簡單方式,然後利用MapReduce叢集對新老資料進行處理再自動地傳回結果。

HBase replication采用的基本架構模式是:master-push;因為每個region server都有自己的write-ahead-log(即WAL或HLog),這樣就很容易記錄下從上次複制之後又發生了什麼,非常類似于其他一些著名的解決方案,就像MySQL 的主從複制就隻用了一個binary log來進行追蹤。一個master叢集可以向任意數目的slave叢集進行複制,同時每個region server會參與複制它本身所對應的一系列的修改。

Replication是異步進行的,這意味着參與的叢集可能在地理位置上相隔甚遠,它們之間的連接配接可以在某段時間内是斷開的,插入到master叢集中的那些行,在同一時間在slave叢集上不一定是可用的(最終一緻性)。

在該設計中所采用的replication格式在原理上類似于MySQL的基于狀态的replication。在這裡,不是SQL語句,而是整個的WALEdits(由來自用戶端的put和delete操作的多個cell inserts組成)會被複制以維持原子性。

每個region server的HLogs是HBase replication的基礎,同時隻要這些logs需要複制到其他的slave叢集上,它們就需要儲存在HDFS上。每個RS會從它們需要複制的最老的log開始讀取,同時為簡化故障恢複會将目前讀取位置儲存到ZooKeeper上。對于不同的slave叢集來說,該位置可能是不同的。參與replication各叢集大小可能不是對稱的,同時master叢集會通過随機化來盡量保證在slave叢集上的replication工作流的平衡。

Figure 8.12. Overview on the replication architecture

HBase Architecture(譯):下 - 星星 - 銀河裡的星星

4.1. Life of a Log Edit

下面的幾節裡會描述下來自用戶端的一個edit從與master叢集通信開始到到達另一個slave叢集的整個生命曆程。

4.1.1. 正常處理流程

用戶端會使用HBase API将Put,Delete和Increment發送給region server。Key values會被region server轉換為一個WALEdit對象。該edit對象會被append到目前的WAL上,同時之後會被apply到它的MemStore中。

通過一個獨立的線程,将該edit對象從log中讀出然後隻保留那些需要複制的KeyValues(也就是說隻保留那些在family schema中隻屬于GLOBAL作用域的family的成員,同時是非中繼資料也就是非.META和-ROOT-)。當buffer被填滿或者讀取者讀到檔案末尾後,該buffer會被随機發送到slave叢集上的某個region server上。region server順序地接受讀到的這些edits,同時将它們按照table放到不同的buffers中。一旦所有的edits讀取完畢,所有的buffer就會通過正常的HBase用戶端進行flush。

回頭再看master叢集的region server,目前複制到的WAL偏移位置會注冊到ZooKeeper上。

4.1.2. Non-responding Slave Clusters

Edit會以同樣的方式進行插入。在獨立的線程中,region server像正常處理過程那樣進行讀取,過濾以及對log edits進行緩存。假設現在所聯系的那個slave叢集的region server不再響應RPC了,這樣master叢集的region server會進行休眠然後等待一個配置好的時間段後再進行重試。如果slave叢集的region server仍然沒有響應,master叢集的region server就會重新選擇一個要複制到的region server子集,然後會重新嘗試發送緩存的那些edits。

與此同時,WALs将會進行切換同時會被存儲在ZooKeeper的一個隊列中。那些被所屬的region server歸檔(歸檔過程基本上就是把一個日志從它所屬的region server的目錄下移到一個中央的logs歸檔目錄下)了的日志會更新它們在複制線程的記憶體隊列中的路徑資訊。

當slave叢集最終可用後,處理方式就又跟正常處理流程一緻了。Master叢集的region server就又開始進行之前積壓的日志的複制了。

4.2. Internals

本節會深入描述下replication的内部操作機制。

4.2.1. 選擇複制到的目标Region Servers

當一個master叢集的region server開始作為某個slave叢集的複制源之後,它首先會通過給定的叢集key聯系slave叢集的ZooKeeper。

該key由如下部分組成:

hbase.zookeeper.quorum

zookeeper.znode.parent

hbase.zookeeper.property.clientPort。

之後,它會掃描/hbase/rs目錄以找到所有可用的sinks(即那些可用接收用于複制的edits資料流的region servers)同時根據配置的比率(預設是10%)來選出它們中的一個子集。比如如果slave叢集有150台機器,那麼将會有15台標明為master叢集的region server将要發送的edits的接受者。因為複制過程中,master的所有region server都會進行,這樣這些slave叢集的region server的負載就可能會很高,同時該方法适用于各種大小的叢集。比如,一個具有10台機器的master叢集向一個具有10%比率的5台叢集的slave叢集進行複制。意味着master叢集的region servers每次都會随機選擇一台機器,這樣slave叢集的重疊和總的使用率還是很高的。

4.2.2. 日志追蹤

每個master叢集的region server在replication znodes體系中都有自己的節點。同時節點下針對每個叢集節點還會有一個znode(如果有5個slave叢集,就會有5個znode建立出來),每個znode下又包含一個待處理的HLogs隊列。這些隊列是用來追蹤由該region server建立的HLogs的,這些隊列的大小可能有所不同。比如,如果某個slave叢集某段時間不可用,那麼這段時間的HLogs就不能被删除,是以它們就得呆在隊列裡(而其他的可能已經處理過了)。具體例子可以參考:the section called “Region Server Failover”。

當一個source被執行個體化時,它會包含region server目前正在寫入的HLog。在log切換時,新的檔案在可用之前就會被添加到每個slave叢集的znode的隊列中。這可以讓所有的sources在該HLog可以append edits之前就能夠知道一個新log已經存在了,但是該操作的開銷目前是很昂貴的。當replication線程無法從檔案中讀出更多的記錄之後(因為它已經讀到了最後一個block),就會将它從隊列中删除,此時要求隊列中的還有其他檔案存在{!還有其他檔案存在就意味着這個檔案是一個已經寫完的日志檔案,而不是正在寫入的那個}。這就意味着如果一個source已是最新狀态,同時複制程序已經到了region server正在寫入的那個log,那麼即使讀到了目前檔案的”end”部分,也不能将它從隊列中删除{!如果該檔案正在被寫入,那麼即使讀到了末尾,也不能認為它已經結束}。

當一個log被歸檔後(因為它不再被使用或者是因為插入速度超過了region flushing的速度導緻目前log檔案數超過了hbase.regionserver.maxlogs的限制),它會通知source線程該log的路徑已經發生改變。如果某個source已經處理完該log,會忽略該消息。如果它還在隊列中,該路徑會更新到相應的記憶體中。如果該log目前正在被複制,該變更會自動完成,讀取者不需要重新打開該被移動的檔案。因為檔案的移動隻是一個NameNode操作,如果讀取者目前正在讀取該log檔案,它不會産生任何異常。

4.2.3. 讀,過濾及發送Edits

預設情況下,一個source會盡量地讀取日志檔案然後将日志記錄盡快地發送給一個sink。但是首先它需要對log記錄進行過濾;隻有那些具有GLOBAL作用域同時不屬于中繼資料表的KeyValues才能保留下來。第二個限制是,附加在每個slave叢集上所能複制的edits清單的大小限制,預設是64MB。這意味着一個具有三個slave叢集的master叢集的region server最多隻能使用192MB來存儲被複制的資料。

一旦緩存的edits大小達到上限或者讀取者讀到了log檔案末尾,source線程将會停止讀取然後随機選擇一個sink進行複制。它會對標明的叢集直接産生一個RPC調用,然後等待該方法傳回。如果成功傳回,source會判斷目前的檔案是否已經讀完或者還是繼續從裡面讀。如果是前者,它會将它從znode的隊列中删除。如果是後者,它會在該log的znode中注冊一個新的offset。如果PRC抛出了異常,該source在尋找另一個sink之前會重試十次。

4.2.4. 日志清理

如果replication沒有開啟,master的logs清理線程将會使用使用者配置的TTL進行舊logs的删除。當使用replication時,這樣是無法工作的,因為被歸檔的log雖然超過了它們自己的TTL但是仍可能在隊列中。是以,需要修改預設行為,在日志超出它的TTL時,清理線程還要檢視每個隊列看能否找到該log,如果找不到就可以将該log删除。查找過程中它會緩存它找到的那些log,在下次log查找時,它會首先檢視緩存。

4.2.5. Region Server故障恢複

隻要region servers沒有出錯,ZooKeeper中的日志記錄就不需要添加任何值。不幸的是,它們通常都會出錯,這樣我們就可以借助ZooKeeper的高可用性和它的語義來幫助我們管理隊列的傳輸。

master叢集的所有region servers互相之間都有一個觀察者,當其中一個死掉時,其他的都能得到通知。如果某個死掉後,它們就會通過在死掉的region server的znode(該znode也包含它的隊列)内建立一個稱為lock的znode來進行競争性選舉。最終成功建立了該znode的region server會将所有的隊列傳輸到它自己的znode下(逐個傳輸因為ZooKeeper并不支援rename操作)當傳輸完成後就會删掉老的那些。恢複後的隊列的znodes将會在死掉的伺服器的名稱後加上slave叢集的id來進行命名。

完成之後,master叢集的region server會對每個拷貝出的隊列建立一個新的source線程。它們中的每一個都會遵守read/filter/ship模式。主要的差別是這些隊列不會再有新資料因為它們不再屬于它們的新region server,同時意味着當讀取者到達最後一個日志的末尾時,隊列對應的znode就可以被删除了,同時master叢集的region server将會關閉那個replication source。

比如,考慮一個具有3個region servers的master叢集,該叢集會向一個id為2的單個slave叢集進行複制。下面的層次結構代表了znodes在某個時間點上的分布。我們可以看到該region servers的znodes都包含一個具有一個隊列的peers znode。這些隊列的znodes的在HDFS上的實際檔案名稱具有如下形式” address,port.timestamp”。

/hbase/replication/rs/ 1.1.1.1,60020,123456780/ peers/ 2/ 1.1.1.1,60020.1234 (Contains a position) 1.1.1.1,60020.1265 1.1.1.2,60020,123456790/ peers/ 2/ 1.1.1.2,60020.1214 (Contains a position) 1.1.1.2,60020.1248 1.1.1.2,60020.1312 1.1.1.3,60020, 123456630/ peers/ 2/ 1.1.1.3,60020.1280 (Contains a position)

現在我們假設1.1.1.2丢失了它的ZK會話,幸存者将會競争以建立一個lock,最後1.1.1.3獲得了該鎖。然後它開始将所有隊列傳輸到它本地的peers znode,同時在原有的名稱上填上死掉的伺服器的名稱。在1.1.1.3清理老的znodes之前,節點分布如下:

/hbase/replication/rs/ 1.1.1.1,60020,123456780/ peers/ 2/ 1.1.1.1,60020.1234 (Contains a position) 1.1.1.1,60020.1265 1.1.1.2,60020,123456790/ lock peers/ 2/ 1.1.1.2,60020.1214 (Contains a position) 1.1.1.2,60020.1248 1.1.1.2,60020.1312 1.1.1.3,60020,123456630/ peers/ 2/ 1.1.1.3,60020.1280 (Contains a position) 2-1.1.1.2,60020,123456790/ 1.1.1.2,60020.1214 (Contains a position) 1.1.1.2,60020.1248 1.1.1.2,60020.1312

一段時間後,但在1.1.1.3結束來自1.1.1.2的最後一個HLog的複制之前,我們假設它也死掉了(而且某些之前建立的新logs還在正常隊列中)。最後一個region server會嘗試鎖住1.1.1.3的znode然後開始傳輸所有的隊列。新的節點分布如下:

/hbase/replication/rs/ 1.1.1.1,60020,123456780/ peers/ 2/ 1.1.1.1,60020.1378 (Contains a position) 2-1.1.1.3,60020,123456630/ 1.1.1.3,60020.1325 (Contains a position) 1.1.1.3,60020.1401 2-1.1.1.2,60020,123456790-1.1.1.3,60020,123456630/ 1.1.1.2,60020.1312 (Contains a position) 1.1.1.3,60020,123456630/ lock peers/ 2/ 1.1.1.3,60020.1325 (Contains a position) 1.1.1.3,60020.1401 2-1.1.1.2,60020,123456790/ 1.1.1.2,60020.1312 (Contains a position)

Replication 目前還是一個處于實驗階段的feature。在将它應用到你的使用場景中時需要進行仔細地評估。

[83] See “B+ trees” on Wikipedia

[84] See LSM-Tree, O’Neil et al., 1996

[85] From “Open Source Search” by Doug Cutting, Dec. 05, 2005.

[86] See the JIRA issue HADOOP-3315 for details.

[87] For the term itself please read Write-Ahead Logging on Wikipedia.

[88] Subsequently they are referred to interchangeably as root table and meta table respectively, since for example

“-ROOT-”

is how the table is actually named in HBase and calling it root table is stating its purpose.

[89] See the online manual for details.