分析資料庫的事務隔離級別在資料庫選型分析的時候很重要

qing_yun發表於2022-09-02

今天寫這篇文章花的時間有點長,分析如此複雜的問題,也難免會有些疏漏和錯誤。如果文中哪些地方寫得不對,請多指正。

昨天一個信創專案要發對十幾個資料庫產品進行評估,需要對資料庫產品設定少量幾個評估項,專案不能太多,但是要能夠反映出資料庫的相容性和通用資料庫能力。我們以前的橫向對比裡面關於這方面的引數指標有二十幾個,要想用兩三個來代表確實有難度。最後想來想去,只選擇了兩個,一個是SQL標準遵循度,一個是事務隔離級別。國產資料庫的SQL標準遵循度一般來說都會說是遵循SQL 1999/SQL 2003等,哪怕SQL引擎是完全使用MYSQL這種對SQL標準遵循度不是很好的資料庫產品,都會號稱支援,不過大多數都會加上一個“核心標準”,可能字比較小,很多人都不會關注,有些產品乾脆就號稱完全支援,現在幾乎沒有一款資料庫能完全支援這些SQL標準,寫完全支援的反而可以直接認為他們在撒謊了。

事務隔離級別可能回讓人感到有些意外,評估事務隔離級別很重要嗎?有可能大多數的企業都還沒有使用標準事務隔離級別以外的其他級別。資料庫系統是個十分複雜的系統,儲存引擎、SQL引擎是其中的兩個十分關鍵的元件,我們要去一點點分析其實現是十分困難的。不過透過事務隔離級別的分析,可以大致瞭解其強一致性與MVCC實現的一些細節。事務隔離級別在SQL1992裡就已經被明確定義了,主要保障關聯式資料庫ACID特性的I(Isolation),既對可能存在衝突的併發事務,提供一定程度的資料安全保證。一般來說資料庫透過鎖和MVCC來實現事務隔離級別,因此分析資料庫的事務隔離級別就可以大致瞭解其鎖和MVCC的實現。

一般來說,有四種最常見的事務隔離級別:1)讀未提交Read UnCommitted;2)讀提交Read Committed;3)重複讀Repeatable Read;4)序列Serializable。

早期的資料庫支援read uncommited,不過這種被稱為髒讀的事務是不安全的,對於現代的應用系統一致性要求無法滿足,因此現代資料庫產品幾乎都不支援這種事務隔離級別了。

讀提交是我們應用系統用到最多的一種事務隔離級別,不過現代資料庫對於讀提交有了更為嚴格的限制,當讀發起的時間點,所有已提交的事務都是可見的,所有未提交的事務都是不可見的。為了實現這一點,資料庫需要有一個嚴格單邊增長的時間戳,這個時間戳和提交有嚴格的關係。凡是事務號低於這個時間戳的事務都是可見的,反之就是不可見的。Oracle把這個時間戳叫做SCN。讀已提交隔離級別是SQL級別的,判斷可見性是根據SQL執行的時間戳。

可重複讀是更高的隔離級別,要求在一個事務中的多個讀都是一致的。事務只能看到事務開始前已提交的資料。既不能看到未提交的資料,也不能看到事務在執行時被其它事務更新的已提交的資料。如果沒有MVCC,這種隔離級別是十分低效的,不過有了MVCC,可以透過副本來避免阻塞寫操作。

序列化是最高的事務隔離級別,也是最低效的一種,因為這要求併發事務序列執行。

絕大多數現代關係型資料庫都支援READ COMMITTED事務隔離級別,這是我們應用最常用的方式。不過Repeatable Read在某些應用場景下也十分有用,可以大大簡化應用開發。同時使用MVCC技術實現的repeated Read,其效率是最高的,比我們在應用中用鎖去控制要高出數個數量級。

事務隔離級別是和併發事務,鎖,MVCC相關。因此在資料庫中評估事務隔離級別的實現方式與效率十分關鍵。Oracle資料庫實際上只支援READ COMMITTED ,Serializable這兩種ANSI定義的事務隔離級別,預設是READ COMMITED。而對於Read Committed,大家也不要簡單的認為只要支援Read Committed的事務隔離級別,我的應用就可以肆無忌憚的把資料一致性都交給資料庫了。實際上Oracle 的Read Committed和ANSI的Read Committed還是不同的,Oracle的Read Committed實際上是Read Consistency,一致性讀。Oracle的讀一致性是基於MVCC機制的,是使用類似SNAPSHOT的方式實現的,其實現基礎是UNDO。因此實際上我們卡宴把Oracle的事務隔離級別看作是基於SQL語句的一致性讀與基於事務的一致性讀(Oracle版的Serializable,與ANSI的也是不同含義)。

MySQL是全部支援ANSI的四種事務隔離級別的,當然是不同的儲存引擎下才完全支援這四種。MySQL的預設事務隔離級別是Repeatable Read,而不是Read Committed,這是因為5.X時代的BINLOG格式導致的,如果你用的是8.X,BINLOG使用ROW格式,那麼最好還是調整為Read Committed。否則在有些應用場景下,死鎖會比較多。大家要注意的是MySQL的Read Committed和Oracle的一致性讀還是有一定區別的,這裡篇幅有限,不做展開,大家有興趣可以去查閱相關資料。

PostgreSQL也是完全支援ANSI的四種事務隔離級別的,只不過預設的事務隔離級別是Read Committed。PG實現事務隔離級別的方式有點類似與Oracle,都是基於Snapshot的,和MySQL的實現方式有些不同。

我們再來看看一些分散式關係型資料庫的事務隔離級別情況。一般來說分散式資料庫是透過2PC(或者變種的改進演算法),GTM等機制來實現分散式事務和MVCC的。不同的資料庫產品實現演算法差異較大,因此分散式資料庫在事務隔離級別上的差異還是很大的。我們先來看幾個頭部的國產分散式資料庫的情況。時間有限,我們重點來看TIDB/OCEANBASE/HOTDB的事務隔離級別。

大家都知道,TIDB的SQL引擎是基於開源的MYSQL程式碼開發的,不過TIDB的事務隔離級別實現要比MYSQL複雜的多,不要用MYSQL的事務隔離級別來看TIDB的事務隔離級別。在理解Tidb的事務隔離級別的時候,一定要注意Tidb是支援樂觀鎖和悲觀鎖的。事務隔離級別在樂觀鎖模式下是不能起到ANSI SQL標準的隔離作用的。我們以下討論Tidb的事務隔離級別都是基於悲觀鎖模式的。TiDB從4.0.0beta開始支援與Oracle基本類似的Read Committed隔離級別。從3.0.8開始,Tidb預設使用悲觀鎖,因此也引入了首個事務隔離級別SI,這是一個類似Oracle的一致性讀,其讀取基線為事務開始時,在整個事務中,事務隔離的特性類似與ANSI模型中的Repeatable Read。Tidb考慮了MySQL使用者的使用習慣,因此也把這種SI隔離模式稱為RR,只不過這個RR類似於Oracle 的Serializable,而不是MYSQL的RR。

OceanBase的企業版分為Oracle租戶和MySQL租戶兩種模式,提供了對這兩種資料庫的相容性支援。開源的社群版只有MySQL租戶模式。

在兩種租戶模式下,OB的事務隔離級別時不同的。這是為了確保從這兩種資料庫中遷移應用過來時候的相容性。OB是一種分散式資料庫,因此其一致性讀依賴於GTS(Global Timestamp Service),要想使用序列化事務隔離級別時,一定要確保GTS是開啟的。

HotDB是透過MySQL提供的外部XA事務來實現分散式事務的強一致性的,因此HotDB的事務隔離級別的實現也是基於MySQL的XA介面的。HotDB支援Repeatable Read、SERIALIZABLE 隔離級別且隔離級別,並且功能表現和單機MySQL相同。透過HotDB實現分散式事務的方式我們也可以看出,與XA分散式事務相沖突的功能,在HotDB中也會收到一定的限制。

最後我們再來看看前陣子有朋友關心的GoldenDB的事務隔離級別問題。GoldenDB的分散式事務實現的方式比較獨特,是透過一階段提交外加自動補償回滾的方式來實現的。沒有嚴格的全域性時間戳機制,只有一個實現Read Committed的全域性事務控制機制。從實現原理上看,GoldenDB的Read Committed的實現進滿足ANSI SQL 1992裡最基本的要求,和Oracle的Read Consistency是不同的,當然Oracle並沒有完全遵循SQL 1999中的標準,而是針對應用系統的要求,提升了RC隔離級別的一致性要求。不過大多數現代資料庫產品都向Oracle靠齊,因此Oracle的RC實際上變成了RC隔離級別的標準了。一個Oracle資料庫的應用向GoldenDB上遷移的時候,要十分關注這一點,否則可能會出現應用邏輯上的問題。

來自 “ 白鱔的洞穴 ”, 原文作者:白鱔;原文連結:https://mp.weixin.qq.com/s/M3R_OqAw0HktHdOGUWWHEg,如有侵權,請聯絡管理員刪除。

相關文章