mysql亂七八糟的可重復讀隔離級別實現

mysql的隔離級別并非是按照標準實現的,作為從pg切過來的程序員還真是不太適應,這篇文章討論mysql隔離級別實現的,希望對大家能有幫助。

什么是事務

事務是數據庫一組讀寫操作的集合,事務具有ACID四個特性,原子性,一致性,隔離性和持久性。
事務有四個隔離級別,分別是讀未提交,讀已提交,可重復讀和串行化。
以上這些內容相信熟悉傳統(tǒng)數據庫的人,對這些都很熟悉,接下來講的內容可能有些人就不太了解了。

事務的實現方式

數據庫事務的實現方式主要有兩種:

  1. 基于鎖的;
  2. 基于時間戳的,現在主流的實現就是基于時間戳的方式的一種,就是大家熟悉的MVCC機制;

因為機制不同,所以事務的表現也不盡相同。

不同機制下的不同隔離級別

SQL標準定義了四種隔離級別,分別是讀未提交,讀已提交,可重復讀,可串行化。很明顯,越低隔離級別的事務并發(fā)行更好,但是一致性更低,嚴格來說,低隔離級別的事務是不符合A和I的,常用的隔離級別多為讀已提交和可重復度。
但是隔離級別的定義是基于鎖并發(fā)控制實現的,基于MVCC機制實現的數據庫事務表現行為會稍有不同。
jim gray曾經有一篇論文討論不同機制實現的數據庫隔離級別的不同表現,并將隔離級別擴展到7個。見下圖:

七種隔離級別.png

基于此將常見的傳統(tǒng)數據庫隔離級別統(tǒng)計如下:

  1. SYBASE支持的隔離級別:degree 0(read uncommitted)、degree 1(read committed)、degree 2(repeatable read)、degree 3(serializable isolation);
  2. ORACLE支持的隔離級別:read committed(consistent read)、serializable(snapshot isolation);
  3. DB2支持的隔離級別:read uncommitted、cursor stability、read stability、repeatable read;
  4. Postgresql支持的隔離級別:read committed(consistent read)、repeatable read(snapshot isolation)、serializable isolation(Serialaizable Snapshot Isolation);
  5. SQL Server支持的隔離級別:read uncommitted、read committed snapshot 、read committed 、repeatable read、snapshot isolation、serializable isolation;
  6. MySQL支持的隔離級別:read uncommitted、read committed(consistent read)、repeatable read(snapshot isolation)、serializable isolation;

幻讀(P3/A3)和寫偏斜(A5B)

上圖的各個字母都是數據庫的各種不一致現象。如果把寫操作記作w,讀操作記作r,那么這些有害依賴可以表示為下圖

identifier query phenomena
P0 w1[x]...w2[x]...((c1 or a1) and (c2 or a2) in any order) Dirty Write
P1 w1[x]...r2[x]...((c1 or a1) and (c2 or a2) in any order) Dirty Read
P2 r1[x]...w2[x]...((c1 or a1) and (c2 or a2) any order) Fuzzy / Non-Repeatable Read
P3 r1[P]...w2[y in P]...((c1 or a1) and (c2 or a2) any order) Phantom
P4 r1[x]...w2[x]...w1[x]...c1 Lost Update
P4C rc1[x]...w2[x]...w1[x]...c1 Lost Update
A3 r1[P]...w2[y in P]...c2....r1[P]...c1 Phantom
A5A r1[x]...w2[x]...w2[y]...c2...r1[y]...(c1 or a1) Read Skew
A5B r1[x]...r2[y]...w1[y]...w2[x]...(c1 and c2 occur) Write Skew

mysql中的可重復度

幻讀

mysql是支持MVCC機制實現的數據庫,因此很多人(包括我)會想當然認為他的SI應該就是標準的實現,不會出現幻讀(A3/P3)的現象。接下來,請看如下例子:

mysql幻讀1-1.jpg

如上圖所示,事務2的insert發(fā)生在兩次select之間,這兩次select也如SI一樣正確的顯示了該看到的結果,但是update發(fā)生之后,一切就變了,MySQL的RR隔離級別也會幻讀?。?!

寫偏斜

也許有人會說,mysql同時也是使用鎖的,因此發(fā)生幻讀不奇怪,所以我們可以看接下來這個寫偏斜的經典例子:

mysql write skew.png

顯然,mysql也是會發(fā)生寫偏斜的。

mysql中可重復讀的實現

看源碼可以發(fā)現,mysql中的讀操作是使用MVCC機制實現,可以正確的查找到需要的行,但是寫操作實現的時候有兩點和我想的不太一樣:

  1. 寫操作永遠讀取已提交的數據,并沒有走MVCC的邏輯;
  2. 寫操作的并發(fā)是通過鎖控制的,不檢查更新行是否是對本事務可見的。

MVCC機制行的可見條件很簡單,可以總結為兩句話:

  1. 對不同事務,插入事務已提交,刪除事務未提交(update可以看做先刪除后插入);
  2. 對本事務,插入的statement發(fā)生在自己之前,刪除的statement未發(fā)生或在自己之后;

套用幻讀那個例子,本來事務1是不該看到新插入的行的(因為不符合可見條件1),但是update只讀取最新的行,因此對新插入的行做了一次更新,導致該行符合可見條件2,再次select就可以查到這個行。

根據這個實現,我們可以推理出,mysql的可重復讀同樣會發(fā)生lost update和read skew,只要測試的事務中存在寫操作。具體例子可見此處

mysql的可重復讀是比SI更低的隔離級別,在發(fā)生幻讀時,SI隔離級別事物的正確行為應該是后提交的事務回滾,而mysql兩個事務都可以提交,顯然,他的一致性更低,但是并發(fā)性更好(回滾率低),這是一次在用戶使用習慣,性能和一致性之間的權衡,至于優(yōu)劣,就見仁見智了,至少現在看來不壞。

postgresql中的可重復讀

無幻讀

pg實現的隔離級別是比較標準的,可重復度級別(實際是SI)沒有幻讀,這里舉兩個例子

第一個例子
pg無幻讀1.jpg

類比mysql的第一個例子,和mysql不同,可以看到pg的事務update的時候只更新了兩行,不包括新插入的行

第二個例子
pg無幻讀2.jpg

當該行同時被兩個可重復級別的事務更新時,后提交的事務會回滾,因為更新只能在最新的行上執(zhí)行,否則就是丟失更新了。

寫偏斜

pg write skew.png

可以看到,pg的可重復級別事務,還是存在寫偏斜的,這是符合標準的。

參考文檔

  1. 《A Critique of ANSI SQL Isolation Levels》
  2. mysql源碼
  3. pg源碼
  4. https://github.com/ept/hermitage/blob/master/mysql.md

廣告

最后,打個廣告,如果對創(chuàng)業(yè),分布式數據庫和開源社區(qū)感興趣,歡迎加入我們,實習和工作都很歡迎!
pingcap是國內為數不多的newsql方向的分布式數據庫,維護國內最頂級的開源社區(qū),關注度近萬,做類f1+spanner架構,和多家公司有合作關系。
TiDB: https://github.com/pingcap/tidb
TiKV: https://github.com/pingcap/tikv
Email: xuwentao@pingcap.com
微信: fbisland

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容