[翻譯]HBase 中的 ACID

同前面翻譯的一篇關聯的,同做者的另外一篇:ACID in HBasehtml

這一篇不是單純地描述一個問題,而是以 ACID 爲主題,介紹了其在 HBase 中各個部分的體現及實現。apache

ACID,即:原子性(Atomicity),一致性(Consistency),隔離性(Isolation),持久性(Durability)。併發

HBase 支持特定場景下的 ACID,即對同一行的 Put 操做保證徹底的 ACID(HBASE-3584增長了多操做事務,HBASE-5229增長了多行事務,但原理是同樣的)oop

那麼 HBase 內部的 ACID 是怎麼樣實現的呢?性能

HBase 實現了一種 MVCC,而且 HBase 沒有混合讀寫事務。優化

因爲歷史的緣由,HBase 的命名有點奇怪(下面會有提到)。翻譯

每一個 RegionServer 擁有嚴格地單調遞增的事務號日誌

一個寫事務(一組 put 或 delete)開始時,該事務獲取下一個最大的事務號,在 HBase 內叫作 WriteNumber。htm

一個讀事務(一個 scan 或 get)開始時,該事務獲取先前最新提交事務的事務號,在 HBase 內叫作 ReadPoint。對象

每一個新建立的 KeyValue 對象用它所在事務的 WriteNumber 標記(因爲歷史的緣由,這個標記在 HBase 內叫作 memstore timestamp,注意這個應用程序層面的、咱們常說的時間戳是兩回事)。

宏觀地,HBase 寫事務的流程是這樣的:

  1. 對行(一行或多行)加鎖,屏蔽對相同行的併發寫;
  2. 獲取當前的 WriteNumber;
  3. 提交修改到 WAL;
  4. 提交修改到 Memstore(用前面獲取的 WriteNumber 標記修改的 KeyValues);
  5. 提交事務,也就是把 ReadPoint 更新爲當前獲取的 WriteNumber;
  6. 釋放行(一行或多行)鎖。

宏觀地,HBase 讀事務的流程是這樣的:

  1. 打開 scanner;
  2. 獲取當前的 ReadPoint;
  3. 用獲取的 ReadPoint 過濾全部掃描到的 KeyValues(KeyValues 的 memstore timestamp > ReadPoint,只看 ReadPoint 以前的);
  4. 關閉 scanner(scanner 由客戶端初始化)。

實際在實現的時候會比上面說的複雜,可是上述也足夠說明問題。注意,reader 在讀的過程當中是徹底不加鎖的,可是咱們依舊保證了 ACID。

須要注意的是:上述的機制只有在事務嚴格順序提交的狀況下管用。不然的話,一個先開始卻未提交的事務將會對一個後開始先提交的事務可見(破壞了隔離性)。可是,HBase 中的事務通常都很短,因此這不是個問題。

HBase 確實實現了:全部事務順序提交。

HBase 中提交一個事務,意味着將當前的的 ReadPoint 更新爲該事務的 WriteNumber,這樣就將事務提交的更改對全部新的 scan 可見。

HBase 維護一個未完成事務的列表,一個事務的提交會被推遲,直到先前的事務提交。注意:HBase 能夠支持併發、全部的修改當即生效,只是提交的時候是順序的。(譯註:這裏實際上隱含地表達了 HBase 性能優先,同時實現的是最終一致性

因爲 HBase 不保證任何 Region 之間(每一個 Region 只保存在一個 Region Server 上)的一致性,故 MVCC 的數據結果只需保存在每一個 RegionServer 各自的內存中。

下一個有趣的事兒,在 compaction 期間發生了什麼?

HBase 的 Compaction 過程,一般將多個小的 store 文件(將 memstore flush 到磁盤時產生)合併成一個大點兒的,並在合併過程當中移除垃圾。這裏的"垃圾"要麼是壽命超過了列族的 TTL(Time-To-Live)或 VERSIONS 設置、要麼是被標記刪除的那些 KeyValues。詳情參見這裏(Deletion in HBase, HBase data rentention options)。

假設在一個 scanner 掃描 KeyValues 過程當中發生了 Compaction,scanner 可能會看到一個不完整的行(HBase 中對行的定義爲:Introduction to HBase),即該行數據不可能從任何一種順序事務調度獲得。(譯註:也就是發生了不一致

HBase 的方案是跟蹤全部打開的 scanner 使用的 ReadPoint 中最先的一個,而後濾掉全部大於該 ReadPoint 的 KeyValues。這一邏輯 連同其它的優化在HBASE-2856中增長進來,這一補丁後,容許 HBase 在併發 flush 的場景下保證 ACID。

HBASE-5569 爲 delete marker 實現了一樣的邏輯(譯註:在 ReadPoint 過濾的邏輯,支持併發刪除場景下的 ACID),HBase 是標記刪除的,故實現了併發刪除的 ACID。

最後,注意,當一個 KeyValue 的 memstore timestamp 比最老的scanner(實際是 scanner 持有的 ReadPoint)還要老時,會被清零(置爲0),這樣該 KeyValue會對全部的 scanner 可見,固然,此時比該 KeyValue 原 memstore timestamp 更早的 scanner 都已經結束了。

額外的幾點:

  • (對於寫事務)即便事務失敗了,ReadPoint 也會被更新,以免阻攔後面等待提交的事務(從實現上說,實際上是同一個過程,沒什麼特別的處理,更新 ReadPoint 的代碼寫在 Java 代碼final{}語句塊內)(譯註:前面提到了HBase 的事務是順序提交的,後面的事務會等待前面的事務提交);
  • 更新寫入到 WAL 後,全部的修改都只建立了一條記錄(record),沒有單獨的 commit record(譯註:我理解這個 commit record 可參考 2階段提交);
  • 當一臺 RegionServer 掛掉,若是 WAL 已經完整寫入,全部執行中的事務能夠重放日誌以恢復,若是 WAL 未寫完,則未完成的事務會丟掉(相關的數據也丟失了)。
相關文章
相關標籤/搜索