昨天看到墨天輪小助手的一道關於Mysql數據庫事務隔離度的問題,突然想到了之前遇到過的一件關於ORACLE數據庫事務隔離度的事兒,以爲能夠幫助你們加深關於數據庫事務隔離的理解,因而整理出來分享給你們。sql
首先講一下事務隔離度。
事務隔離度(Transaction isolation models)並非個別數據庫提出的概念,而是一個國際化標準( ANSI and ISO/IEC)。全部數據庫(不單單是傳統的關係數據庫,例如OARCLE,Mysql,SQL Server,也包括NoSQL數據庫,例如TiDB,OceanBase等等)都要在這個標準上設計,區別在於支持哪一個(或那些)模式。它標識一個數據庫系統能在什麼程度上保證「讀一致性」的能力。數據庫
下面用一個簡單的小例子說明一下事務隔離度(Transaction isolation models)。有兩個事物,事物A負責更新表T1,事物B負責查看錶T1。session
事物A:
T1.c1=0-> 更新T1 (c1=1) ---> Commit ---> 更新T1 (c1=2) ---> Commit->ide
事物B:
----- t1 --------------------> t2 ---------> t3 ---------------- >t4 --------->t5this
假設事物B從 t1 時刻開始。
若是他在 t1 時刻看到 T1.c1=0 ,t2 時刻看到 T1.c1=1 ,在 t3 時刻看到 T1.c1=1 ,在 t4 時刻看到 T1.c1=2,在 t5 時刻看到 T1.c1=2,那麼就是Read uncommitted。
若是他在 t1 時刻看到 T1.c1=0 ,t2 時刻看到 T1.c1=0 ,在 t3 時刻看到 T1.c1=1 ,在 t4 時刻看到 T1.c1=1,在 t5 時刻看到 T1.c1=2,那麼就是Read committed。
若是他在 t2 時刻看到 T1.c1=0 ,t2 時刻看到 T1.c1=0 ,在 t3 時刻看到 T1.c1=1 ,在 t4 時刻看到 T1.c1=1,在 t5 時刻看到 T1.c1=1,那麼就是Repeatable read。
若是他在 t2 時刻看到 T1.c1=0 ,t2 時刻看到 T1.c1=0 ,在 t3 時刻看到 T1.c1=0 ,在 t4 時刻看到 T1.c1=0,在 t5 時刻看到 T1.c1=0,那麼就是Serializable read。spa
而後來回答一下墨天輪小助手的問題。
MySQL數據庫是全面支持上面4種事務隔離度的,默認隔離度是「Repeatable read」。設計
使用命令:SET session TRANSACTION ISOLATION LEVEL xxxx; 能夠修改事務隔離度。
(參數能夠爲:Read uncommitted,Read committed,Repeatable,Serializable)code
那麼ORACLE數據庫呢?blog
ORACLE數據庫只支持 Read committed 和 Serializable read 兩種事務隔離度,默認隔離度是「Read committed」。事務
參考文檔: Master Note: Oracle Transaction Management (Local) Overview (ドキュメントID 1506115.1) ------------------------------------------------------------------------------------------------------------------ Oracle database provides Read committed and Serializable isolation levels with "Read committed" as the default. In addition, Oracle database also provides another isolation level - Read-only isolation level, which is similar to the Serializable level but doesn't allow DML statements in the transaction(except for SYS). These isolation levels can be set at the session level using the "SET TRANSACTION..." command ------------------------------------------------------------------------------------------------------------------
那這件事兒到底有啥實際影響呢?給你們講一個這樣的事例。
ORACLE數據庫裏有一種叫作「Materialized View」(物化視圖)的Object。它提供一種能夠自動或手動的把一個表的數據同步到另一個表(物化視圖)的方法。這個同步的過程就叫作「Refresh」。
若是有不少物化視圖須要手動Refresh時,一個一個的刷新顯然是比較麻煩的,因而ORACLE提供了把多個物化視圖編組(Group),而後一塊兒Refresh的方法。
ORACLE數據庫還有一種叫作「Foreign Key」(外鍵約束)的東西。它提供一種兩個表之間的數據約束機能。具體是怎麼約束的,相信你們都知道,這篇文章裏再也不贅述。咱們只須要知道主表中沒有的數據是不可能在外鍵表中存在的。
如今有這樣一個場景:
1.有兩個具備外鍵約束的表:T1是主表,T2是外鍵表。 2.這兩個表都有一個物化視圖:MV10 和 MV20。 3.有一個處理給T1和T2插入記錄。由於T1,T2之間存在外鍵約束,必須先對T1插入數據,Commit。而後對T2插入數據,Commit。 4.在處理3進行的同時,另一個Session對物化視圖Group MV10 ~ MV20進行Refresh。
你們想一下,在上面的場景中,有沒有可能主表T1的物化視圖 MV10不存在,而在外鍵表T2的物化視圖 MV20中存在的記錄呢??
答案是:有可能。
由於Mview Group進行Refresh時,刷新順序是Mview名的字母順。在上面的場景中就是先刷新 MV1 ,再刷新 MV2
參考文檔: Materialized Views (MVIEWs) Refresh Order Using "DBMS_SNAPSHOT.REFRESH" LIST Parameter; 9i Vs 10g and Higher Versions (ドキュメントID 1452382.1) ------------------------------------------------------------------------------------------------------------------ From 10g onwards, it refreshes in alphabetical order, i.e the refresh starts with "MVIEWa" instead of "MVIEWi" and ends with "MVIEWk" instead of "MVIEWa". Because of this, dependency MVIEWs are not getting correct data. ------------------------------------------------------------------------------------------------------------------
爲了解釋上面的緣由,我仍是畫這樣的圖例:
事物3: ---> 更新T1 (insert into t1 values(1);) ---> Commit ---> 更新T2 (insert into t2 values(1);) ---> Commit --->
事物4: ----> Refresh MV10 ----> Refresh MV11 ----> Refresh MV12-----> …略… -----> Refresh MV19------> Refresh MV20-------->
由於ORACLE數據庫的默認事務隔離度是「Read committed」。
也就是說刷新主表T1的物化視圖 MV10時,主表T1並無Commit,事物4看不見事物3的更新。等到刷新外鍵表T2的物化視圖 MV20時,主表T1和外鍵表T2都已經Commit,事物4看見了事物3的更新。
因此,上面的現象看起來是不合理的(外鍵表的Mview裏有數據而主表的Mview裏沒有數據),但倒是符合式樣動做的(expected behavior)。
根據上面的事項,ORACLE最後也公佈了官方文檔。
參考文檔: MVIEW of Child Table is Refreshed but MVIEW of Parent Table with Foreign Key Constraint is not Refreshed (ドキュメントID 2697569.1) ------------------------------------------------------------------------------------------------------------------ Cause It's an expected behavior, as describe in Doc ID 1452382.1 If a group of materialized views are refreshing with "DBMS_SNAPSHOT.REFRESH" LIST Parameter, Oracle will refresh the MVIEWs in alphabetical order. So if the Data inserts and Mview refresh operations are at the same, Because of the commit and refresh timing, MVIEW of Parent Table may be not Refreshed with some new data. Reference: Materialized Views (MVIEWs) Refresh Order Using "DBMS_SNAPSHOT.REFRESH" LIST Parameter; 9i Vs 10g and Higher Versions (Doc ID 1452382.1) Solution Don't Insert data to base table and Refresh the mview at the same time. ------------------------------------------------------------------------------------------------------------------
可是這個問題也能夠經過設置事物4的SESSION隔離度來回避。
例如:set transaction isolation level serializable;