Atomicity (原子性) :
定義: 用戶發起屢次寫請求: 中間某次寫失敗則全部寫操做都被回滾數據庫
實現方式: 災害恢復日誌網絡
Consistency (一致性):
定義: 應用層面的不變性在事務中被保持併發
隔離級別: 解決事務併發對數據產生影響的問題
常見的併發問題:分佈式
隔離級別 | 描述 | 髒讀 | 不可重複讀 | 幻影讀 | 實現方式 |
---|---|---|---|---|---|
serializable | 併發的事務等同於事務一個個執行,徹底沒有併發問題,可是性能最差 | false | false | false | 實際序列化執行, 二段鎖, 可序列化快照分離 |
repeatable read | 快照分離(snapshot isolation): 事務中讀取事務開始前的提交數據,即便其餘事務在期間改動數據並提交 | false | false | true | MVCC (Multi-version concurrency control) 記錄數據的多個版本,找到事務開始前的數據版本 |
read committed | 事務提交的數據才能被讀到, 寫操做只能覆蓋提交的數據 | false | true | true | 防止髒寫: 對事務中寫對象持有行級鎖直到事務結束. 防止髒讀: 記錄數據的2個版本,沒法解決計數器更新丟失問題 |
Durability (持久性):
定義: 事務提交以後數據被保存到持久化存儲,不會由於宕機而丟失ide
實現方式:
單機: 先寫日誌(write ahead log)
集羣: 保證寫操做被持久化到多個節點性能
處理複雜對象如JSON等, 須要保證原子性和隔離性
原子性經過先寫日誌 (write ahead log) 保證
隔離性經過鎖或者compare and swap, 有的數據庫提供原子性的read-modify-write操做如increment
不一樣於事務, 事務通常是針對多個不一樣對象的處理
許多分佈式數據庫由於實現上的困難只保證單對象寫而放棄多對象事務
多對象事務仍然在如下場景須要:
關係型數據庫:確保外鍵一致性被正確保持
文檔型數據庫: 因爲數據被DENORMALIZE,一般一次更新會涉及多個文檔,須要保證多個文檔被同時正確更新
存在索引的狀況下, 保證數據更新和索引的一致性優化
放棄執行到一半的事務並重試
重試可能存在的問題:
事務成功執行但由於網絡錯誤沒法獲得事務結果:
若是錯誤是由於系統超載,重試只會讓問題變得更糟
重試通常能解決暫時問題, 死鎖,網絡異常,隔離級別錯誤等, 若是是永久性錯誤引發的問題(違反外鍵約束等), 重試沒有意義
重試不能解決事務中數據庫以外額外影響, 須要輔助二段式事務等方式
重試中若是客戶進程崩潰會致使數據丟失線程
寫數據時,修改的數據被TAG惟一自增交易ID(用於斷定交易發生前後), 例如transactionID = 13的事務修改ID爲1的數據從V1->V2, 再修改ID爲2的數據從V1->V2, 爲每次操做生成修改前和修改後兩條交易版本數據:
id=1, dataValue=V1, createdBy=5, deleteBy=13
id=1, dataValue=V2, createBy=13, deleteBy=nil
id=2, dataValue=V1, createdBy=5, deleteBy=13
id=2, dataValue=V2, createdBy=13, deleteBy=nil
MVCC數據可見規則:日誌
換而言之, 若是一個對象對於讀事務可見: 對象
MVCC索引實現和優化:
問題定義: 兩個事務同時讀取數據,進行更新,再將數據寫回,其中一個事務的寫結果被另外一個覆蓋. 好比計數器更新
解決方法:
在集羣複製場景下(多主複製或無主複製): 比較寫入方法不能正常工做, LAST WRITE WIN策略會形成寫丟失. 原子化更新有效
衝突實體化 (materialize conflict): 將寫衝突轉化爲一系列的主鍵鎖. 好比建立預訂房間記錄(房間號+時間範圍),並對預訂記錄加鎖. 將併發控制處理滲漏到業務邏輯中,不推薦.
實際序列化執行 (actual serialization):
實現方式:
二階段鎖 (two phase locking):
實現方式:
缺點:
前提鎖 (predicate lock):
鎖符合查詢條件的對象,讀寫鎖獲取和二階段鎖相似
索引範圍鎖 (index range lock):
比predicate lock範圍大,不精確匹配全部查詢條件而匹配其中某個索引查詢,以下午1:00-2:00對某個房間的預訂. 直接對房間號加鎖或者對時間範圍加鎖.
若是沒有找到合適的索引,對全表加共享鎖.
可序列化快照分離 (serializable snapshot isolation):
實現方式:
根據事務開始前預設定的前提決定事務是否能提交, (好比每一個房間/時間段預訂數<=1). 而後檢查查詢結果是否被改變.
事務1讀到一箇舊的MVCC版本(數據已經被另外一個事務修改但未提交), 再提交時發現新的MVCC版本已經被事務2提交, 而且後續事務1也對這個數據進行了修改. 則事務1必須回滾
事務1開始後事務2修改了數據. 並早於事務2提交. 若是後續事務1也對這個數據進行了修改. 則事務1必須回滾
我的閱讀事務章節的一點感想:事務控制無非兩種: 樂觀: 經過沖突檢測決定事務是否回滾. 悲觀: 經過加鎖阻止併發發生.從另外一個維度看, 不一樣的隔離級別決定了兩個事務對於數據庫的讀寫操做是否阻塞,隔離級別越低,阻塞的場景越少任何隔離級別一個事務的讀操做不會阻塞讀讀阻塞寫, 寫阻塞讀 (Serializable級別 -- two phase locking)寫阻塞讀, 但讀不阻塞寫寫阻塞寫 ()