讓我從以下2個例子展開我們的探討。
- Case1:
- HELLODBA.COM>set time on
- 10:22:09 HELLODBA.COM>update t_test1 set SECONDARY='A' where object_id = -1;
- 1 row updated.
- 10:22:22 HELLODBA.COM>commit;
- Commit complete.
- Session 1:
- 10:22:25 HELLODBA.COM>update t_test1 set SECONDARY='B' where object_id = -1 and SECONDARY='B' and (select count(*) from t_test2 t1, t_test2 t2) > 0;
- 0 rows updated.
- 10:23:15 HELLODBA.COM>
- Session 2:
- 10:22:37 HELLODBA.COM>update t_test1 set SECONDARY='B' where object_id = -1;
- 1 row updated.
- 10:23:02 HELLODBA.COM>commit;
- Commit complete.
- 10:23:04 HELLODBA.COM>
- Case2:
- 10:25:38 HELLODBA.COM>update t_test1 set SECONDARY='A' where object_id = -1;
- 1 row updated.
- 10:25:48 HELLODBA.COM>commit;
- Commit complete.
- Session 1:
- 10:26:05 HELLODBA.COM>update t_test1 set SECONDARY='B' where object_id = -1 and SECONDARY='A' and (select count(*) from t_test2 t1, t_test2 t2) > 0;
- 0 rows updated.
- 10:27:21 HELLODBA.COM>
- Session 2:
- 10:26:16 HELLODBA.COM>update t_test1 set SECONDARY='B' where object_id = -1;
- 1 row updated.
- 10:26:41 HELLODBA.COM>commit;
- Commit complete.
- 10:26:42 HELLODBA.COM>
如果你觀察得足夠仔細,你可以從上面2個例子看到一個有趣的現象:無論session 1是否命中到資料,it最終都沒有修改資料。其根本原因就是當前模式讀與一致性讀的區別。
我們知道,為了減少並發衝突,Oracle引入了MVCC多版本並發控制,也叫MCC)方法。在這種機制中,並發事務不會因為一致性的原因而相互阻塞,除非他們要修改同一條記錄。他們會將日誌中所有SCN大於本身事務SCN的日誌做復原,以保證本事務讀取到的資料區塊與事務SCN的一致。在Oracle中,這樣的讀取行為就稱為一致性讀。
然而,一致性讀所讀取到資料區塊僅僅是某個時間點的一個快照,也就是說這樣的資料是唯讀。如果要修改資料,那麼oracle需要讀取到當前的資料區塊,也就是當前模式讀。
在一個UPDATE過程中,oracle會先一致性讀取與事務SCN一致的資料快照,並用where條件進行過濾。讓後根據讀取到資料區塊的ID,再從當前資料中讀取到相應的資料區塊進行修改。但是,如在事務啟動後到資料區塊被讀取之間的這段時間內,相應的資料區塊發生了改變,那麼可能就會有我們意想不到的事情發生。
往回看我們的第一個例子。我們在session 1中,在10:22:25啟動了update事務。但是,由於該事務中存在一個大的子查詢,它會在幾十秒後才會讀取到需要被修改的資料。在Session 2中,我們在10:22:37開始update這些資料並在10:23:02提交了事務。而這個時間是早於資料在session 1中被讀取到的時間的。當session 2中的資料改變被提交後,session 1中的事務讀取到了該資料區塊。因為session 2中的事務SCN大於session 1中的事務SCN,因此會讀取UNDO中的資料進行復原,也就是說它讀取到資料SECONDARY是'A',再通過條件(SECONDARY='B')過濾後,沒有資料被命中,因此也沒有資料被修改。
在第二個例子中,session 1的事務在一致性讀取到資料區塊之前也發生了類似的事情。當它復原了資料後,它一致性讀取到了滿足過濾條件(SECONDARY='A')的資料區塊。此時,它需要通過該資料區塊ID再到當前資料中讀取該資料區塊。但是因為當前資料區塊的內容已經被session 2中的事務所修改,它還是沒有能修改到資料。
我想,通過這兩個例子,讀者應該更容易理解到當前模式讀與一致性讀之間的區別。