Hbase事務


原文:http://hbasefly.com/2017/07/26/transaction-2/

1、關於hbase事務

HBase目前只支持行級事務; 可以保證行級數據的原子性、一致性、隔離性以及持久性,即通常所說的ACID特性。 為了實現事務特性,HBase采用了各種並發控制策略,包括各種鎖機制、MVCC機制等。


2、hbase事務原子性

hbase寫數據:HBase數據會首先寫入WAL,再寫入Memstore。 寫入Memstore異常很容易可以回滾,因此保證寫入/更新原子性只需要保證寫入WAL的原子性即可。HBase 0.98之前版本需要 保證WAL寫入的原子性並不容易,這由WAL的結構決定。假設一個行級事務更新R行中的3列(c1, c2, c3),來看看之前版本 和當前版本的WAL結構: 1. 之前版本WAL結構: <logseq1-for-edit1>:<KeyValue-for-edit-c1>

<logseq2-for-edit2>:<KeyValue-for-edit-c2>

<logseq3-for-edit3>:<KeyValue-for-edit-c3> 每個KV都會形成一個WAL單元,這樣一行事務更新多少列就會產生多少個WAL單元。在將這些WAL單元append到日志文件的時候,一旦出現宕機或其他異常,
就會出現部分寫入成功的情況,原子性更新就無法保證。 2. 當前版本WAL結構: <logseq#-for-entire-txn>:<WALEdit-for-entire-txn>

<logseq#-for-entire-txn>:<-1, 3, <Keyvalue-for-edit-c1>, <KeyValue-for-edit-c2>, <KeyValue-for-edit-c3>> 通過這種結構,每個事務只會產生一個WAL單元。這樣就可以保證WAL寫入時候的原子性。


3、hbase事務強一致性保證

* 寫寫並發控制 * 批量寫入多行的寫寫並發 * 讀寫並發控制


為什么需要寫寫並發控制 :

現在假設有兩個並發寫入請求同時進來,分別對同一行數據進行寫入。下圖所示RowKey為Greg,現在分別更新列族info下的Company列和Role列:

image


如果沒有任何並發控制策略的話,寫入數據(先寫WAL,再寫memstore)可能會出現不同KV寫入”交叉”現象,如下圖所示:

image


這樣的話,用戶最終讀取到的數據就會產生不一致,如下:

image


如何實現寫寫並發控制:

實現寫寫並發其實很簡單,只需要在寫入(或更新)之前先獲取行鎖,如果獲取不到,說明已經有其他線程拿了該鎖,就需要 不斷重試等待或者自旋等待,直至其他線程釋放該鎖。拿到鎖之后開始寫入數據,寫入完成之后釋放行鎖即可。這種行鎖機制 是實現寫寫並發控制最常用的手段,后面可以看到MySQL也是使用行鎖來實現寫寫並發的。


如何實現批量寫入多行的寫寫並發:

HBase支持批量寫入(或批量更新),即一個線程同時更新同一個Region中的多行記錄。那如何保證當前事務中的批量寫入與 其他事務中的批量寫入的並發控制呢?思路還是一樣的,使用行鎖。但這里需要注意的是必須使用兩階段鎖協議,即: (1) 獲取所有待寫入(更新)行記錄的行鎖 (2) 開始執行寫入(更新)操作 (3) 寫入完成之后再統一釋放所有行記錄的行鎖 不能更新一行鎖定(釋放)一行,多個事務之間容易形成死鎖。兩階段鎖協議就是為了避免死鎖,MySQL事務寫寫並發控制同 樣使用兩階段鎖協議。


4、讀寫並發控制

為什么需要讀寫並發控制:

現在我們通過在寫入更新之前加鎖、寫入更新之后釋放鎖實現寫寫並發控制,那讀寫之間是不是也需要一定的並發控制呢?

如果不加並發控制,會出現什么現象呢?接着看下圖:

image


上圖分別是兩個事務更新同一行數據,現在假設第一個事務已經更新完成,在第二個事務更新到一半的時候進來一個讀請求,

如果沒有任何並發控制的話,讀請求就會讀到不一致的數據,Company列為Restaurant,Role列為Engineer,如下圖所示:

image

可見,讀寫之間也需要一種並發控制來保證讀取的數據總能夠保持一致性,不會出現各種詭異的不一致現象。


如何實現讀寫並發控制:

實現讀寫並發最簡單的方法就是仿照寫寫並發控制 – 加鎖。但幾乎所有數據庫都不會這么做,性能太差,對於讀多寫少的應用
來說必然不可接受。那還有其他方法嗎?

當然,這就是今天要重點提到的MVCC機制 – Mutil Version Concurrent Control。HBase中MVCC機制實現主要分為兩步:

(1) 為每一個寫(更新)事務分配一個Region級別自增的序列號

(2) 為每一個讀請求分配一個已完成的最大寫事務序列號

示意圖如下所示:

image

上圖中兩個寫事務分別分配了序列號1和序列號2,讀請求進來的時候事務1已經完成,事務2還未完成,因此分配事務1對應的序列號1給讀請求。

此時序列號1對本次讀可見,序列號2對本次讀不可見,讀到的數據是:

image


具體實現中,所有的事務都會生成一個Region級別的自增序列,並添加到隊列中,如下圖最左側隊列,其中最底端為已經提交 的事務,隊列中的事務為未提交事務。現假設當前事務編號為15,並且寫入完成(中間隊列紅色框框),但之前的寫入事務還 未完成(序列號為12、13、14的事務還未完成),此時當前事務必須等待,而且對讀並不可見,直至之前所有事務完成之后才 會對讀可見(即讀請求才能讀取到該事務寫入的數據)。如最右側圖,15號事務之前的所有事務都成功完成,此時Read Point 就會移動到15號事務處,表示15號事務之前的所有改動都可見。

image


可能有朋友有疑問:如果這兩個自增序列是同一個序列,那是不是這個隊列的順序必須與事務寫入WAL的順序一致? 如果不一致有什么問題?如果要求一致的話怎么才能實現? 千萬不要查看1.1.2的代碼,會把你徹底搞混的!建議閱讀更高版本的相關代碼! 所以,MVCC的精髓是寫入的時候分配遞增版本信息(SequenceId),讀取的時候分配唯一的版本用於讀取可見,比之大的版 本不可見。這里需要注意版本必須遞增,而且版本遞增的范圍一定程度上決定了事務是什么事務,比如HBase是Region級別的 遞增版本,那么事務就是region級別事務。MySQL中版本是單機遞增版本,那么MySQL事務就支持單機跨行事務。Percolator 中版本是集群遞增版本,那么Percolator事務就是分布式事務。


5、hbase事務持久性保證

HBase事務持久化可以理解為WAL持久化, 目前實現了多種持久化策略:SKIP_WAL,ASYNC_WAL,SYNC_WAL,FSYNC_WAL。 SKIP_WAL表示不寫WAL,這樣寫入更新性能最好,但在RegionServer宕機的時候有可能會丟失部分數據; ASYNC_WAL表示異步將WAL持久化到硬盤,因為是異步操作所以在異常的情況下也有可能丟失少量數據; SYNC_WAL表示同步將WAL持久化到操作系統緩存,再由操作系統將數據異步持久化到磁盤,這種場景下RS宕掉並不會丟失 數據,當操作系統宕掉會導致部分數據丟失; FSYNC_WAL表示WAL寫入之后立馬落盤,性能相對最差。目前實現中FSYNC_WAL並沒有實現!用戶可以根據業務對數據丟 失的敏感性在客戶端配置相應的持久化策略。







免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM