同前面翻譯的一篇關聯的,同做者的另外一篇: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 寫事務的流程是這樣的:
宏觀地,HBase 讀事務的流程是這樣的:
實際在實現的時候會比上面說的複雜,可是上述也足夠說明問題。注意,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 都已經結束了。
額外的幾點: