PostgreSQL的事務隔離分析

瘋狂的餅乾發表於2016-06-15

事務隔離

不懂的同學先補補概念
Reference: WiKi

隔離級別(Isolation levels)

有四種隔離級別:

  • 可序列化(Serializable)
  • 可重複讀(Repeatable reads)
  • 提交讀(Read committed)
  • 未提交讀(Read uncommitted)

正文

昨天被問了一個問題
當存在表test(id int)並有id=1一條記錄, 那麼以下兩種操作會有什麼行為

  1. SessionA啟動事務後,SessionB做了更新id=2操作後,此時SessionA進行 UPDATE test SET id=id+2,結果如何。
  2. SessionA啟動事務後,SessionB在事務中做了更新id=2操作後,先不提交,此時SessionA進行 UPDATE test SET id=id+2,結果如何。

順著原始碼分析一下Postgres的是如何實現這種行為的。

提交讀(PostgreSQL的預設設定)

在提交讀的隔離級別下,
1.行為a的結果,SessionA 進行更新操作前查詢id變為2,更新操作成功後,查詢id為4。
2.行為b的結果,在SessionB提交前,SessionA的查詢id為1,如進行update操作,會一直阻塞直到SessionB提交或回滾,如SessionB成功提交查詢id為4,若SessionB回滾,查詢id結果為3。

行為1分析

SessionA的執行流程和普通的更新流程類似,先得到需要更新的row,然後進入heap_update,對選定的row使用HeapTupleSatisfiesUpdate進行版本(MVCC)檢查,由於SessionB的事務已經提交,所以會得到HeapTupleMayBeUpdated的狀態,然後真正進行更新操作。(其中包括hot-update等各種流程就不在此描述)

行為2分析

此時的執行流程會和上面略有不同,當獲取目標row時候會得到尚未更新的那行row(因為此row雖然被標記為已刪除,但是因為SessionB尚未提交,所以仍然可見),對row進行更新版本檢查時,發現此row已經刪除,且SessionB還未提交,標記為HeapTupleBeingUpdated,接著嘗試取得該row的鎖(會等待直到SessionB提交或者回滾),之後檢查此row,如果被更新成功(SessionB提交),則進行row的refresh,對refresh後的row重新進行之前的操作,如果更新失敗(SessionB回滾),則直接更新。

可重複讀

在可重複讀的隔離級別下,
1.行為a的結果, SessionA 進行更新操作前查詢id為1,若進行更新操作, 則報錯 “could not serialize access due to concurrent update”。
2.行為b的結果, 在SessionB提交前,SessionA的查詢id為1,如進行update操作,會一直阻塞直到SessionB提交或回滾,如SessionB成功提交則報錯 “could not serialize access due to concurrent update”, 若SessionB回滾,則sessionA 的 UPDATE操作成功,查詢id結果為3,

行為1分析

和提交讀隔離級別的行為有點類似,但由於是可重複讀的快照,所以一開始取得的目標row是更新前的row,也就是id=1(提交讀id=2)的行,於是在更新操作的mvcc版本檢查中會認為此row是HeapTupleUpdated狀態,需要重新refresh row,在refresh對隔離級別進行檢查,如果大於等於可重複讀的級別,則拋錯。

行為2分析

和提交讀隔離級別的程式碼路徑一致,只是在 refresh row 時 對隔離級別進行檢查,因為此時為可重複讀,所以拋錯。


相關文章