續 Day 6。
強列建議閱讀本文之前要先去看 Day 4 - Snapshot Isolation。
難道 序列化隔離 (serializable isolation) 跟良好效能是矛盾的嗎?
在這裡跟您介紹 序列化快照隔離 (Serializable Snapshot Isolation) ,一種比快照隔離多了一點開銷的算法,但它提供了完整的序列化功能,它來自於 2009 年 Michael Cahill's 的博士論文。
二階段鎖 (Two-Phase Locking) 看上去可以被稱為悲觀的並發控制機制,悲觀的大原則是說凡是有可能錯的就一定會出錯,也就是像 2PL 那樣要取得 互斥模式 (exclusive mode) 鎖才能寫入資料,其他人最好還是乖乖等我!
而 連續執行 (Serial Execution) 更是悲到極致了,它本質上等同於每個 transaction 都得獲取整個資料庫的互斥鎖才能做事情。
相比之下序列化快照隔離就是屬於樂觀的並發控制機制,樂觀代表它不假設潛在的危害會發生,它希望一切都會好轉;當一個 transaction 想要 commit 時,資料庫才會檢查是否有不好的事情在發生(例如隔離無效),如果有,則該 transaction 會中斷且重試。
就像命名那樣,序列化快照隔離的基礎是快照隔離,所有的讀取都是來自有一致性的資料庫快照中 (Day 4 - Snapshot Isolation),而最大的不同是序列化快照隔離多新增了一個演算法,在寫入期間去檢測是否發生連續的衝突然後決定哪個 transaction 要被中斷。
如同我們在 Day 5 提過的,快照隔離可能會發生 write skew ,其模式就是 查詢-判斷-寫入,在 commit 時其原始查詢結果可能 不再是最新的,因為該資料可能同時被其他 transaction 修改。
換一種方式來說,這些 transaction 是基於一種 前提 來執行,以 Day 5 - 醫生 oncall 案例 來看,前提就是:目前正有 2 個醫生在 oncall
,爾後在 commit 時,這個前提可能就不是為真了。
但資料庫怎麼知道應用程式對查詢結果的判斷邏輯為何呢?為了安全起見,資料庫會假設所有的查詢結果都可能都會被改變(查詢結果 = 前提),意味者該 transaction 的寫入可能會無效。
資料庫必須檢測 transaction 的前提是否過時,所以這裡有 2 個狀況需要考慮:
回憶一下 MVCC 的機制,當 transaction 從一致的快照讀取資料時,它會忽略其他未 commit 的 transaction 寫入,如下圖 7-10 (使用 Day 5 - 醫生 oncall 案例),transaction 43 讀取到的資料是 Alice on_call=true
,因為 transaction 42 還沒 commit,然而,當 transaction 43 想要 commit 時,transaction 42 已經 commit 了,這意味者 transaction 43 commit 會被忽略,transaction 管理者會注意到快照資料已被影響,且 前提 的值不再是最新的了。
現在來看第 2 個情況,如下圖 7-11,transaction 42 和 43 都查詢了 shift_id=1234
的班,如果這裡有個 shift_id
的 index,資料庫就會借用 index 進入點 1234 來記錄 transaction 42 和 43 正在讀取資料(如果沒有 index 就會追蹤在 table 等級中),其資料會保留到所有並發 transaction 都結束後才會清除。
當 transaction 寫入到資料庫時 ,它必須看一下 index 資料內是否也有正在讀取的資料被影響,transaction 42 和 43 會彼此留意資料有可能不再是最新的,所以 transaction 43 想要 commit 時,transaction 42 已 commit 所以發生衝突,故 transaction 43 被中斷了。
樂觀並發控制在有非常高度競爭時(多個 transaction 嘗試存取相同物件)會表現的比較不好,代表了會有很多的片段 transaction 會需要被中斷。
但是跟 二階段鎖 (Two-Phase Locking) 比起來,序列化快照隔離有個最大的優勢就是 transaction 的寫入不用阻檔等待從其他 transaction 取得鎖,就像快照隔離那樣,寫入跟讀取不會互相影響,這個設計原則讓查詢延遲 (lantency) 變的可預測且變化較小,read-only 類型的查詢可以從一致的快照讀取資料而不需要取得任何鎖,適合用在讀取重的場景上。
而跟 連續執行 (Serial Execution) 比較呢?序列化快照隔離不必被限制上單一 CPU core 的吞吐量多少了。
影響序列化快照隔離效能最大的就是中斷的比率,所以它當然也希望所有 transaction 都是短小精幹(長時間只讀不寫的 transaction 也許 Ok);總而言之,它對緩慢 transaction 的敏感度遠遠小於 二階段鎖 跟 連續執行。
transaction 重要,但更重要的是挑選適合業務規則的資料庫,希望 Day 1~Day 7 的 transaction 能幫助你們更了解一點。
總結這麼短是因為字數夠了 XD