1算法
分佈式事務背景數據庫
隨着分佈式數據庫技術的發展愈來愈成熟,業內對於分佈式數據庫的要求也由曾經只用知足解決海量數據的存儲和讀取這類邊緣業務向核心交易業務轉變。分佈式數據庫若是要知足核心帳務類交易需求,則其須要完善分佈式事務,向傳統關係型數據庫看齊。即分佈式事務的實現也須要像傳統關係型數據庫的事務同樣知足事務的標準要求及定義,即ACID特徵。緩存
分佈式數據庫的數據是進行多機器多節點分散存儲的,這樣的存儲架構爲實現分佈式事務帶來了極大的難度。數據事務操做時,事務操做會結合數據分佈狀況,到不一樣的存儲位置上去執行,而這個存儲位置位於網絡中的不一樣機器的不一樣磁盤上。服務器
2網絡
事務基本概念架構
2.1 事務使用場景
銀行應用是一個經典案例,能夠解釋事務應用的必要性。假設銀行數據庫有兩張表,支票帳戶表(check)和存款帳戶表(save)。如今要從LiLei的支票帳戶裏轉帳200元到她的存款帳戶,那麼須要至少完成3步操做:併發
檢查支票存款帳戶的餘額是否大於200元;異步
從支票存款帳戶餘額中減去200元;分佈式
全部的操做被打包在一個事務裏執行,若是某一步失敗,就回滾全部已完成步驟。事務操做通常用 START TRANSACTION 語句開始一個事務,用 COMMIT 語句提交整個事務,永久地修改數據,或者用 ROLLBACK 語句回滾整個事務,取消已作的修改。事務SQL操做樣例以下:ide
START TRANSACTION;
SELECT balance FROM check WHERE customer_id = 10233276 ;
UPDATE check SET balance = balance - 200.00 WHERE customer_id = 10233276;
UPDATE save SET balance = balance + 200.00 WHERE customer_id = 10233276;
COMMIT;
此爲銀行對於轉帳類的交易所必須使用的事務操做場景,而在實際的生產環境中,事務操做的複雜度比這複雜得多。
2.2 事務概念和特性
事務是訪問及操做數據庫各種數據項的操做序列集合,如各種增刪改查 SQL 操做組合。它一般由 begin transaction 和 end transaction 語句來界定。
數據庫系統的事務需包含如下特性:
原子性(Atomicity):事務的全部操做在數據庫中要麼所有執行成功,要麼所有執行失敗。
一致性(Correspondence):事務操做先後,數據的完整性必須保持一致。
隔離性(Isolation):多個用戶併發訪問數據庫時,數據庫爲每一個用戶開啓事務,不能被其餘事務的操做數據所幹擾。即每一個事務都感受不到系統中有其餘事務在併發地執行。
持久性(Durability):一個事務成功完成後,它對數據庫的改變必須是永久的,即便出現系統故障也不會對事務有影響。
事務隔離級別
針對事務隔離,SQL標準定義了4類隔離級別,包括了一些具體規則,用來限定事務內外的哪些改變是可見的,哪些是不可見的。下面介紹四種隔離級:
READ UNCOMMITTED(讀取未提交內容)
在READ UNCOMMITTED隔離級別,全部事務均可以「看到」未提交事務的執行結果。讀取未提交數據,也被稱之爲「髒讀」。
READ COMMITTED(讀取提交內容)
大多數數據庫系統的默認隔離級是read committed。它知足了隔離的早先單定義:一個事務在開始時,只能「看見」已經提交事務所作的改變,一個事務從開始到提交前,所作的任何數據改變都是不可見的,除非已經提交。此隔離級別不支持「可重複讀」的操做。這意味着用戶運行同一語句兩次,看到的結果是不一樣的。
REPEATABLE READ (可重讀)
REPEATABLE READ隔離級解決了READ UNCOMMITTED隔離級致使的問題。它確保同一事務的多個實例在併發讀取數據時,會「看到一樣的」數據行。不過理論上,這會致使另外一個棘手問題:幻讀(Phantom Read)。簡單來講,幻讀指當用戶讀取某一範圍的數據行時,另外一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的數據行時,會發現有新的「幻影」行。數據庫存儲引擎能夠經過多版本併發控制 (Multiversion Concurrency Control)機制解決了幻讀問題,如MySQL的InnoDB和Falcon。
SERIALIZABLE (可串行化)
SERIALIZABLE是最高級別的隔離級,它經過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。簡言之,SERIALIZABLE是在每一個讀的數據行上加鎖。在這個級別,可能致使大量的超時現象和鎖競爭現象。數據庫應用中不多看到有用戶選擇這種隔離級。但若是用戶的應用爲了數據的穩定性,須要強制減小併發的話,也能夠選擇這種隔離級。
3
分佈式事務
分佈式事務的實現須要保證事務的原子性、一致性、隔離性和持久性,而實現此ACID屬性的基本技術思路有:
經過「兩階段提交(Two-phase Commit,2PC)」協議實現事務的原子性、一致性和持久性等屬性;
隔離性級別的實現一般使用多版本併發控制機制來保證。實現多版本併發控制經常使用的方式是「快照隔離(Snapshot Isolation)」技術;
下面先分別介紹一下這兩個概念。
3.1 兩階段提交
兩階段提交(Two-phase Commit,2PC)是爲了使基於分佈式系統架構下的全部節點在進行事務提交時保持一致性而設計的一種協議。
兩階段提交算法的成立基於如下假設:
該分佈式系統中,存在一個節點做爲事務協調器,其餘節點做爲事務管理器,且節點之間能夠進行網絡通訊。
全部節點都採用預寫式日誌(Write Ahead Log),且日誌被寫入後即被保持在可靠的存儲設備上,即便節點損壞不會致使日誌數據的消失。
全部節點不會永久性損壞,即便損壞後仍然能夠恢復。
如下對二階段提交算法分階段進行說明。
第一階段(提交請求階段)
事務協調器節點向全部事務管理器節點詢問是否能夠執行提交操做,並開始等待各事務管理器節點的響應。事務管理器節點執行詢問發起爲止的全部事務操做,並將Undo信息和Redo信息寫入日誌。
各事務管理器節點響應事務協調器節點發起的詢問。若是事務管理器節點的事務操做實際執行成功,則它返回一個「贊成」消息;若是事務管理器節點的事務操做實際執行失敗,則它返回一個「停止」消息。有時候,第一階段也被稱做投票階段,即各事務管理器投票是否要繼續接下來的提交操做。
第二階段(提交執行階段)
成功的狀況
當事務協調器節點從全部事務管理器節點得到的相應消息都爲「贊成」時:
事務協調器節點向全部事務管理器節點發出「正式提交」的請求。
事務管理器節點正式完成操做,並釋放在整個事務期間內佔用的資源。
事務管理器節點向事務協調器節點發送「完成」消息。
失敗的狀況
若是任一事務管理器節點在第一階段返回的響應消息爲「停止」,或者事務協調器節點在第一階段的詢問超時以前沒法獲取全部事務管理器節點的響應消息時:
事務協調器節點向全部事務管理器節點發出「回滾操做」的請求。
事務管理器節點利用以前寫入的Undo信息執行回滾,並釋放在整個事務期間內佔用的資源。
事務管理器節點向事務協調器節點發送「回滾完成」消息。
有時第二階段也被稱做完成階段,由於不管結果怎樣,事務協調器都必須在此階段結束當前事務。
事務協調器和事務管理器之間的通訊流程的示意圖:
兩階段提交算法的最大缺點就在於:它的執行過程當中間,節點都處於阻塞狀態。即節點之間在等待對方的相應消息時,它什麼也作不了。特別是,當一個節點在已經佔有了某項資源的狀況下,爲了等待其餘節點的響應消息而陷入阻塞狀態時,當第三個節點嘗試訪問該節點佔有的資源時,這個節點也將連帶陷入阻塞狀態。
另外,事務協調器節點指示事務管理器節點進行提交等操做時,若有事務管理器節點出現了崩潰等狀況而致使事務協調器始終沒法獲取全部事務管理器的響應信息,這時事務協調器將只能依賴事務協調器自身的超時機制來生效。但每每超時機制生效時,事務協調器都會指示事務管理器進行回滾操做。這樣的策略顯得比較保守。
3.2 快照隔離
快照隔離(Snapshot Isolation)技術是實現多版本併發控制的技術之一。此技術策略的前提就是每條數據都要支持版本化,事務對數據的每次寫操做成功提交後(更新、插入、刪除)都會生成該數據的一個新版本。這裏有一個概念,就是寫操做成功提交以後,纔會生成數據的新版本。在寫操做沒有成功提交以前,對數據的任何修改,都不算生效的。
什麼是快照Snapshot呢?簡單來講就是在某個特定時刻T1,數據庫裏面全部數據最新版本的集合。舉個例子,好比數據庫裏面只有3條記錄,它們在時間戳T1的時候,狀態以下:
即 row1[version 10],row2[version = 1], row3[version=19] 就造成了數據庫在T1時刻的快照。過了幾分鐘以後,到了時間T2,若是在T1和T2之間沒有寫操做成功提交,那麼數據庫的狀態沒有變化,即T1時候的快照和T2時候的快照是相等的。再過了幾分鐘以後,到了時間T3,在T2和T3之間,有一條對row2的更新操做、一條對row3的刪除操做、一條row4的插入操做成功提交了,數據庫中的數據狀態變成了:
即 row1[version 10],row 2[version = 2], row3[version=20],row4[version=1] 就構成了數據庫在T3時間的快照。由於每條記錄的版本變動是不同的,因此須要注意數據版本的變動狀況。
另外,請注意在數據多版本的要求下,刪除操做並非真的刪掉row3,而是生成了一個row3的新版本。在實際實現中,數據庫不必定是按上面示例的同樣把值賦值成null,也可能用一個特殊的標誌位標識這是一個「刪除」的版本。
快照永遠和特定的時間相關,脫離時間談論快照是沒有意義的。若是在一段時間內,數據庫沒有任何寫操做成功提交,那麼這段時間內,數據庫在任意時間的快照都是相等的。因此,咱們能夠認爲,每個有包含寫操做的事務成功提交,都會造成數據庫的一個不一樣的快照。在不少數據庫實現中,version直接使用時間戳,而不是上面例子中的數字。
每一個事務在啓動時,都會記錄當時的時間做爲啓動時間戳Start-Timestamp。該事務只能讀取啓動時間戳那個時刻的數據快照。而後每一個事務在提交時,會記錄當時時間做爲提交時間戳Commit-Timestamp,當該事務成功提交後,會造成一個Commit-Timestamp的數據快照。後續啓動的事務才能看到該事務寫的數據(若是該事務有寫操做)。
上圖中,三條橫線表明三個事務。事務T2是看不到事務T1寫的任何數據的,由於事務T2啓動時,事務T1尚未提交。而事務T3能夠看到事務T1和事務T2寫的數據,由於它啓動的時候,事務T1和事務T2都提交了。
快照隔離(Snapshot Isolation)須要經過鎖機制來防止寫衝突,對於讀操做,不加鎖。若是多個事務同時寫一個數據,鎖機制保證最多隻有一個事務能提交成功。因爲對讀操做不加鎖,Snapshot Isolation的性能會顯著提升。
4
SequoiaDB 分佈式事務實現
4.1 基本概念和定義
爲了實現分佈式事務,巨杉數據庫經過採用全局時間來實現全局事務對跨數據分片的事務的協調和管理。基於此需求,爲了肯定全局時間,巨杉數據庫定義了時間戳的相關概念與定義,引入了時間戳管理機制。具體的定義以下:
LLT(Local Logical Timestamp):每一個節點(CATALOG、COORD、DATA)維護本身的本地邏輯時間(最小單位:microsecond)
ULT(Universal Logical Timestamp): 定義CATALOG主節點的本地時間爲全局邏輯時間(最小單位:microsecond)
LRT(Local Real Timestamp):本地UTC時間
爲了保證整個集羣全局時間的一致與準確,協調節點(COORD)和數據節點(DATA)須要定時與編目節點(CATALOG)的主節點進行時間同步。而同步時間定義瞭如下規則:
1. CATALOG主節點的LLT(即ULT)經過全部機器的CPU Tick計算
2. 其它節點的LLT經過與CATALOG主節點進行同步ULT來維護
同步的間隔爲ULTSyncInterval(默認:60秒)
5. ULTTolerance根據時間差同步、網絡狀態進行動態調整
全局時間的定義及規則確認以後,則能夠將其用於分佈式事務的實現當中。分佈式事務採用二段提交機制實現,結合二段提交的原理,定義瞭如下幾類事務時間:
TBT(Transaction Begin Timestamp):事務開始時間
TPCT(Transaction Pre-Commit Timestamp):事務的預提交(precommit)時間
TCP(Transaction Commit Timestamp):事務的提交時間
其中,同一個事務的TBT和TPCT之間須要有一個事務時間間隔,此間隔取當時ULTTolerance。事務時間間隔也能夠定義爲不一樣節點發起的事務時間之間的最小能夠容忍的偏差。即若是兩個不一樣節點的事務時間之間相關小於事務時間間隔,即認爲這兩個事務時間有偏差的狀況下相等。
4.2 二段提交實現
巨杉數據庫對於分佈式事務採用的是經典二段提交(2PC)方式實現的。其採用全局時間來實現全局事務的統一協調管理,使分佈式集羣中的不一樣節點進行事務的統一操做。在整個事務操做過程當中,客戶端發起的事務分爲三個部分:
第一部分:事務開始。在這一部分的操做中,客戶端向數據庫服務器發起「事務開始」的請求。數據庫服務器結合其本地邏輯時間生成一個事務開始時間,並記錄在案。
第二部分:事務的增刪改查操做。此部分是整個事務原子包的系列操做,它包含增刪查改四類基本數據操做。在執行事務原子包裏面第一條SQL語句時,分佈式集羣須要判斷和校驗協調節點和數據節點之間的時間差值。若是此差值大於延時容忍值,則要求COORD節點、DATA節點向CATALOG主節點發起時間同步,而後再從新發起SQL操做。若是時間差在容忍範圍內,則直接執行。第一條事務操做執行成功後,說明時間比對成功,接下來的操做則直接執行。
第三部分:事務完成。此部分爲事務的結束部分。在此部分中,整個事務執行完成,開始發起事務提交的操做。此操做進入事務的二段提交階段,即先預提交,預提交成功以後再提交一次,整個提交流程才完成。
巨杉數據庫事務實現的具體流程以下圖:
4.3 併發控制技術
巨杉數據庫對於多版本控制(MVCC)技術是經過採用事務鎖、內存老版本以及磁盤迴滾段重建老版本的設計來實現。此架構設計的理論基礎是經過對內存結構的合理利用,存儲數據和索引的老版本信息,從而實現數據的快速的併發訪問。
此架構的基本原則是:充分利用內存結構緩存老版本以提升讀的訪問速度,同時結合事務可視性條件和MVCC來知足全局事務的不一樣隔離級別(RC/RR)的訪問要求。在MVCC的實現中,巨杉數據庫也平衡兼顧運行時的效率和多版本存儲空間的使用,以及回收的開銷。
在多版本控制技術的事務鎖實現中,RR(可重複讀)配置下的讀操做能夠在使用完記錄以後當即釋放鎖,不須要一直持有,直到事務提交或者回滾。可是寫事務操做則須要一直持有插入、更改和刪除的鎖,直到事務完成提交或者回滾。巨杉數據庫鎖的實現是採用悲觀鎖機制,與傳統關係型數據庫的採用的主流鎖機制相似。
在多版控制技術的實現中,除了引入悲觀鎖的機制之外,巨杉數據庫還採用了內存老版本機制提高數據庫併發訪問及操做的能力。內存老版本是經過在記錄鎖上附加有一個存儲原版本數據和索引相關的結構,於內存中存儲了老版本的數據。
全部事務寫操做(修改,刪除,插入)會在該結構中保存一個事務開始前的記錄的拷貝,還包含全部改動過索引的原始版本。當讀者試圖獲取記錄鎖時,若是記錄正在被修改,讀者取鎖失敗時將經過回調函數得到該鎖的老版本結構,從而獲取上次提交後的數據。在事務提交時,釋放記錄鎖以後異步回收存儲老版本記錄和索引的空間,用戶能夠選擇打開異步刪除涉及到的待刪除數據。同時在該鎖或記錄被下一個寫操做用到時,他們都會被同步回收。其中老版本的結構以下:
巨杉數據庫在實現多版本併發控制技術時,除了採用事務鎖和內存老版本機制外,還採用了磁盤迴滾段對併發控制策略進行了完善與補充。衆所周知,內存是高速存儲設備,可是其存在存儲空間比較小以及斷電數據丟失的問題。針對此問題,磁盤迴滾段機制經過將內存中的「老版本數據」持久化到磁盤上,保證數據庫在掉電等異常狀況下不會影響事務的正常操做。
回滾段使用系統集合空間,名爲」SYSRBS」。另外,其內部會使用1個集合,命名格式爲」SYSRBSXXXX」,其中XXXX爲循環編號,範圍爲0~4096。同時,回滾段使用第一個集合(即:SYSRBS0000)存儲RBS的元數據,包括當前RBS集合和最後空閒RBS集合。巨杉數據庫會在啓動時檢查是否支持MVCC,若是支持,則會檢查」SYSRBS」集合空間是否存在,不存在的話則會建立此集合空間,同時建立 SYSRBSCL0000 和 SYSRBSCL0001 集合。若是回滾段的集合空間和集合均存在,則會從 SYSRBSCL0000 中讀取元數據信息,根據當前RBS集合和最後空閒RBS集合信息建立下一下 SYSRBSCLXXXX。
爲了更進一步提升讀取速度,巨杉數據庫將磁盤迴滾段與內存老版本相結合,最新的老版本仍是掛在記錄鎖的 oldversionContainer 上,其它更老的版本放磁盤上。這樣知足大多數據短事務只用讀內存的老版本,無需再讀磁盤,從而提供了讀取速度。考慮到主節點異常的狀況,多版本控制須要將記錄老版本數據的回滾段也同步至備節點,當備節點升爲主節點後,能夠經過回滾段重建老版本。
當事務ID小於全局最小事務ID(lowTranID)時,數據庫後臺的異步線程負責回收老版本記錄和索引節點內存。內存老版本清理時要將其保存的老版本寫入RBS。而磁盤老版本的清理則是從最後空閒集合(lastFreeCL)開始,逐個對比表的最大事務ID(MaxGTID),若是小於全局最小事務ID,則能夠刪除這個表(即SYSRBSCLXXXX)。
5
總結
巨杉數據庫經過採用事務鎖、內存老版本以及磁盤迴滾段重建老版本的設計來實現了多版本併發控制技術。此設計經過對內存結構的合理利用,存儲數據和索引的老版本信息,從而實現多版本數據的快速的併發訪問。