MySQL Group Replication Multi-Primary 模式,你真的理解么?
MySQL 數(shù)據(jù)庫支持傳統(tǒng)的四大事務(wù)隔離級別,即 Read Uncommitted(RU)、Read Committed(RC)、Repeatable Read(RR)、Serializable(SRZ)。
然而,對于 MySQL Group Replication Multi-Primary 模式,嚴(yán)格來說他的事務(wù)隔離級別是快照事務(wù)隔離級別(Snapshot Isolation,下簡稱:SI)。
今天,姜老師來聊聊數(shù)據(jù)庫的快照事務(wù)隔離級別。
Snapshot Isolation 概述
很多 DBA 同學(xué)在面試的時候會被問到事務(wù)的隔離級別,然后會說Read Uncommitted、Read Committed、Repeatable Read、Serializable。
是的,但這四大事務(wù)隔離級別的定義姜老師更喜歡稱其為古典事務(wù)隔離級別,即 ANSI SQL 92 定義的事務(wù)隔離級別。
ANSI SQL 92 定義的事務(wù)隔離級別依次解決 Dirty Read(臟讀),Repeatable Read(不可重復(fù)讀)、Phatom(幻讀)。
在當(dāng)時的學(xué)術(shù)界,認(rèn)為只要解決了這三個問題,那么事務(wù)就是具有真正的隔離性。
最終推導(dǎo)出具有兩階段加鎖的 Serializable 事務(wù)隔離級別可以保證完整的隔離性。
The fundamental serialization theorem is that well-formed two-phase locking guarantees serializability.
但是!后來 Hal Berenson、Jim Gray 他們在 1995 年發(fā)表了一篇新的論文《A critique of ANSI SQL Isolation levels》[1],用來批判 ANSI SQL的事務(wù)隔離級別。
在論文的摘要中,可以看到如下內(nèi)容:
ANSI SQL-92 [MS, ANSI] defines Isolation Levels in terms of phenomena: Dirty Reads, Non-Repeatable Reads, and Phantoms. This paper shows that these phenomena and the ANSI SQL definitions failto characterize several popular isolation levels, including the standard locking implementations of the levels. Investigating the ambiguities of the phenomena leads to clearer definitions; in addition new phenomena that better characterize isolation types are introduced. An important multiversion isolation type, Snapshot Isolation, is defined.
在論文的總結(jié)部分,可以看到如下內(nèi)容:
In summary, there are serious problems with the original ANSI SQL definition of isolation levels.
可以說,這篇論文應(yīng)該基本上把 Jim Gray 在自己書中 《Transaction Processing: Concepts and Techniques》[2],對于事務(wù)隔離級別的定義進(jìn)行”徹頭徹尾“的批判。
論文的大意是除了 ANSI SQL 定義的三種并發(fā)問題,還有其他并發(fā)問題,如 Lost Update(P4)、Read Skew(A5A)、Write Skew(A5B)、New Phantom(A3B)等。
之前定義的事務(wù)隔離級別,除了 SRZ,都無法解決。
然后論文引出了新的事務(wù)隔離級別 SI ,相比之前除了 SRZ,SI 有著更好的隔離性。
Such applications will find Snapshot Isolation better behaved than either: it avoids the lost update anomaly, some phantom anomalies (e.g., the one defined by ANSI SQL),it never blocks read only transactions, and readers do not block updates.
為了簡潔說明,這里僅舉例 Read Skew 的問題,看下面的測試用例:
可以看到事務(wù) T1 讀取到了事務(wù)T2修改后的數(shù)據(jù),因此不符合隔離性的要求。但是這個場景不在 ANSI SQL 定義的 Dirty Read,Repeatable Read、Phatom 范疇內(nèi)。上述場景就是論文中定義的 Read Skew:
A5A Read Skew Suppose transaction T1 reads x, and then a second transaction T2 updates x and y to new values and commits. If now T1 reads y, it may see an inconsistent state, and therefore produce an inconsistent state as output. In terms of histories, we have the anomaly:
A5A: r1[x]...w2[x]...w2[y]...c2...r1[y]...(c1 or a1)
在 RC、RR 隔離級別下,是無法解決上述問題的,只有通過設(shè)置隔離級別為 SRZ。
但是 SRZ 隔離級別需要遵循兩階段加鎖,即對每條讀取到的記錄加鎖,可能會被寫操作堵塞,因此使用 SRZ 隔離級別后,數(shù)據(jù)庫并發(fā)性能較差。
然而, SI 事務(wù)隔離級別不會有 Read Skew問題,同時讀取操作也不會阻塞變更操作。
簡單來說,SI 有著更好的隔離性,以及比 SRZ 更好的性能,甚至可以比肩 RC 事務(wù)隔離級別。
BTW,論文中談及的 (Basic)SI 隔離級別也沒有解決 Write Skew 的問題。
但是在之后的論文《Serializable isolation for snapshot databases》[3], SSI (Serializable Snapshot Isolation) 徹底達(dá)到了事務(wù)隔離性的要求。
Snapshot Isolation 實現(xiàn)原理
論文《A critique of ANSI SQL Isolation levels》對于 SI 的實現(xiàn)原理做了大致介紹,原理還是非常簡單的,主要是以下幾個主要過程:
(1)事務(wù)T1讀取第一條記錄時,分配一個 Start-Timestamp ,這個值是單調(diào)遞增的;
(2) 任何事務(wù)修改的記錄會被寫入到快照中,以便事務(wù)需要訪問這些歷史記錄版本;
(3) 當(dāng)事務(wù)T1提交時,會獲得一個 Commit-Timestamp 。事務(wù) T1 能提交的前提是不存在其他事務(wù)T2,修改事務(wù) T1 中的變更的任何一條記錄。原文:
The transaction successfully commits only if no other transaction T2 with a Commit-Timestamp in T1’s execution interval [Start-Timestamp, Commit-Timestamp] wrote data that T1 also wrote.
(4) 若事務(wù)提交時存在沖突,哪個事務(wù)先提交,則提交成功,這個機(jī)制稱為:First-committed-wins。
從上面的實現(xiàn)原理看,SI 本質(zhì)是一種樂觀鎖的機(jī)制,讀不會因為寫操作而阻塞,寫只有在提交時才會進(jìn)行沖突檢測。
所以,若每個事務(wù)絕大部分情況下更新的記錄都不沖突,則 SI 隔離級別有著極好的性能表現(xiàn),也就是前面說的性能不輸 RC。
那么,SI 就完美無缺了么?它的缺點是什么呢?
其實在生產(chǎn)環(huán)境中,他的缺點是比較致命的。
一方面,他需要假設(shè)事務(wù)絕大部分情況下更新的記錄都不沖突,若存在熱點,如類似秒殺這樣的場景,則 SI 的性能會嚴(yán)重退化。
另一方面,對于每條記錄的快照需要保存在內(nèi)存中,以類似 RowVersion + Start-Timestamp 的形式存在。如若存在大事務(wù),則需要較大的內(nèi)存使用。因此,支持 SI 隔離級別的數(shù)據(jù)庫,如 PostgreSQL 數(shù)據(jù)庫,需要設(shè)置使用 SSI 隔離級別的內(nèi)存使用量。當(dāng)然,這個問題新版本 PG 貌似已經(jīng)解決[3]。
MGR 與 SI
講了這么多 SI 的知識點,其實現(xiàn)在大家就會發(fā)現(xiàn) MGR Multi-Primary 模式的隔離級別本質(zhì)是 SI。
首先,雖然 MGR Multi-Primary 模式是 Share Nothing 的架構(gòu),但其允許在多個節(jié)點中并發(fā)寫入數(shù)據(jù),我們要將 MGR 集群看成一個大的數(shù)據(jù)庫實例。
其次,MGR Multi-Primary 模式是一種樂觀鎖機(jī)制,多個事務(wù)在并發(fā)提交的時,會在各節(jié)點上進(jìn)行全局沖突檢測。若存在事務(wù)之間有更新同一行的記錄情況,則回滾事務(wù)。沖突檢測的原理是基于WriteSet,回滾的原則依然遵循 First-committed-wins。
最后,MGR 需要嚴(yán)格控制事務(wù)大小,當(dāng)事務(wù)太大時,Certification_info 會占用大量的內(nèi)存,從而導(dǎo)致系統(tǒng)的不穩(wěn)定。參數(shù) group_replication_transaction_size_limit 用于控制事務(wù)大小,類似對于 SI 隔離級別內(nèi)存使用上限的控制。
MySQL 源碼也有對 SI 實現(xiàn)的簡單說明:
總結(jié)來說,以前單實例數(shù)據(jù)庫的 SI 隔離級別,事務(wù)提交時的沖突檢測僅在單個進(jìn)程中完成。
但對于 MGR Multi-Primary 這樣的集群來說,事務(wù)交前,會通過 Paxos 協(xié)議發(fā)送 Certification_info 到 MGR 中的每個節(jié)點,然后再進(jìn)行沖突檢測。
其中,Certification_info 是一個map,由 <WriteSet、GTID>組成。
typedef std::unordered_map<std::string, Gtid_set_ref *> Certification_info;
再次提醒,對于 SI 事務(wù)隔離級別來說,提交時可能會失敗。
即在 MGR Multi-Primary 模式下,正常的提交可能失?。。?!
ERROR 3101 (HY000) at line 4: Plugin instructed the server to rollback the current transaction.
一方面,在 MGR 中,事務(wù)提交失敗并不代表數(shù)據(jù)庫發(fā)生了故障,業(yè)務(wù)需要有重試邏輯(理解樂觀鎖機(jī)制)。
另一方面,業(yè)務(wù)側(cè)一定要做好對于上述錯誤碼的監(jiān)控,如果很多,則表示你的 MGR Multi-Primary 模式使用姿勢存在很大的問題。
總結(jié)
MySQL Group Replication Multi-Primary 是目前為止關(guān)系型數(shù)據(jù)庫最偉大的產(chǎn)品,但很多同學(xué)并不能充分發(fā)揮其優(yōu)勢。
這就如拿著特斯拉 Model S 當(dāng)燃油車去跑賽道,最后分?jǐn)?shù)肯定不理想。
所以,理解 SI 隔離級別,是理解 MySQL Group Replication Multi-Primary 的第一步,也是充分發(fā)揮 MGR 全面潛力的第一步。
MGR,你準(zhǔn)備好了么?
參考文獻(xiàn):
[1]. https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-95-51.pdf
[2]. J. Gray and A. Reuter, “Transaction Processing: Concepts and Techniques”
[3]. https://courses.cs.washington.edu/courses/cse444/08au/544M/READING-LIST/fekete-sigmod2008.pdf
[4]. https://drkp.net/papers/ssi-vldb12.pdf