溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

變與不變: Undo構(gòu)造一致性讀的例外情況

發(fā)布時間:2020-08-10 07:14:57 來源:ITPUB博客 閱讀:156 作者:數(shù)據(jù)和云 欄目:關(guān)系型數(shù)據(jù)庫

嘉年華聽了恩墨學(xué)院的一個主題:《重現(xiàn)ORA-01555 細說Oracle 12c Undo數(shù)據(jù)管理》,呂星昊老師介紹了UNDO的概念以及ORA-1555的產(chǎn)生,并介紹了12c以來Oracle的UNDO相關(guān)的新特性。

其中介紹了Oracle如何使用UNDO來實現(xiàn)多版本一致性讀,使用了OPEN CURSOR的方式非常巧妙地在很少量數(shù)據(jù)的情況下構(gòu)造出可重現(xiàn)的案例。不過這個案例存在一點小的瑕疵,因為如果一不小心,很可能會導(dǎo)致結(jié)果與預(yù)期不符,這是因為這里有一個例外存在。

我們先來模擬一下UNDO構(gòu)造一致性讀的情況,對于Oracle而言,默認的隔離級別是READ COMMIT,也就是說一個會話只能看到其他會話已經(jīng)提交的修改,未提交的修改或者在當(dāng)前會話查詢發(fā)起之后提交的修改都是不可見的。

再介紹一下OPEN CURSOR,Oracle中當(dāng)一個游標(biāo)被打開,其結(jié)果集就已經(jīng)確定了,也就是說這個游標(biāo)會根據(jù)OPEN CURSOR這個時間點對應(yīng)的SCN來構(gòu)造一致性查詢。但是OPEN CURSOR時,對應(yīng)的SQL并不會被執(zhí)行,在后續(xù)FETCH的時候(對于SQLPLUS而言PRINT命令會觸發(fā)FETCH),SQL才真正被執(zhí)行。使用這種辦法可以模擬一個大的查詢,OPEN CURSOR相當(dāng)于大的查詢的開始時間,其早于其他會話的修改提交時間,而FETCH的時間相當(dāng)于大查詢讀取到這條記錄的時間,而該時間晚于其他會話提交的時間:

SQL> SET SQLP 'SQL1> '

SQL1> CREATE TABLE T_UNDO (ID NUMBER, NAME VARCHAR2(30));

Table created.

SQL1> INSERT INTO T_UNDO SELECT ROWNUM, OBJECT_NAME FROM DBA_OBJECTS;

96920 rows created.

SQL1> COMMIT;

Commit complete.

SQL1> CREATE INDEX IND_UNDO_ID ON T_UNDO(ID);         

Index created.

SQL1> SELECT NAME FROM T_UNDO WHERE ID = 1119;

NAME

------------------------------------------------------------

I_EXTERNAL_LOCATION1$

SQL1> VAR C REFCURSOR

SQL1> EXEC OPEN :C FOR SELECT NAME FROM T_UNDO WHERE ID = 1119;

PL/SQL procedure successfully completed.

 在第一個會話已經(jīng)構(gòu)造了一個查詢,下面在會話2對這條ID為1119的記錄進行修改并提交:

SQL> SET SQLP 'SQL2> '

SQL2> UPDATE T_UNDO SET NAME = 'UPDATED' WHERE ID = 1119;

1 row updated.

SQL2> COMMIT;

Commit complete.

 在會話3上執(zhí)行查詢,這時會看到會話2修改提交后的結(jié)果:

SQL> SET SQLP 'SQL3> '

SQL3> SELECT NAME FROM T_UNDO WHERE ID = 1119;

NAME

------------------------------------------------------------

UPDATED

 回到會話1,對CURSOR變量執(zhí)行PRINT,檢查得到的結(jié)果:

SQL1> PRINT :C  

NAME

------------------------------------------------------------

I_EXTERNAL_LOCATION1$

 到目前為止,所有都是預(yù)期之內(nèi)的結(jié)果,Oracle會利用UNDO來存儲UPDATE的前鏡像,當(dāng)查詢發(fā)現(xiàn)需要訪問的數(shù)據(jù)塊SCN大于會話發(fā)起的SCN,而需要通過UNDO中存儲的前鏡像來構(gòu)造一致性讀,找到會話需要讀取的修改前的數(shù)據(jù)。

那么例外來自哪里呢,在這個例子中,我們給ID列上創(chuàng)建了一個索引,如果這不是一個普通的索引,而是一個主鍵,那么效果如何呢:

SQL1> DROP INDEX IND_UNDO_ID;

Index dropped.

SQL1> ALTER TABLE T_UNDO ADD PRIMARY KEY (ID);

Table altered.

SQL1> SELECT NAME FROM T_UNDO WHERE ID = 1118;

NAME

------------------------------------------------------------

EXTERNAL_LOCATION$

SQL1> EXEC OPEN :C FOR SELECT NAME FROM T_UNDO WHERE ID = 1118;

PL/SQL procedure successfully completed.

會話2修改ID為1118的記錄:

SQL2> UPDATE T_UNDO SET NAME = 'UPDATED WITH PK' WHERE ID = 1118;

1 row updated.

SQL2> COMMIT;

Commit complete.

會話3檢查確認修改結(jié)果:

SQL3> SELECT NAME FROM T_UNDO WHERE ID = 1118;

NAME

---------------

UPDATED WITH PK

再次回到會話1,PRINT游標(biāo)變量:

SQL1> PRINT :C

NAME

------------------------------------------------------------

UPDATED WITH PK

可以看到例外產(chǎn)生了,一致性讀的結(jié)果被破壞了,居然可以查詢到發(fā)生在游標(biāo)打開之后提交的修改。

導(dǎo)致這個例外的原因來自于一個隱含函數(shù)_row_cr:

變與不變: Undo構(gòu)造一致性讀的例外情況

 Oracle11g以后,這個隱含參數(shù)默認值修改為TRUE,這使得Oracle對于基于主鍵的訪問不再采用默認的一致性讀方案。當(dāng)然Oracle做出這種修改的目的是為了提高性能,而且僅對于單行訪問生效,而大部分情況下單行訪問的效率非常高,因此對于一致性破壞的影響并不明顯。到18C為止,該參數(shù)仍然為TRUE。

如果關(guān)閉該參數(shù):

SQL1> ALTER SYSTEM SET "_row_cr" = FALSE;

System altered.

SQL1> SELECT NAME FROM T_UNDO WHERE ID = 1117;

NAME

------------------------------------------------------------

I_EXTERNAL_TAB1$

SQL1> EXEC OPEN :C FOR SELECT NAME FROM T_UNDO WHERE ID = 1117;

PL/SQL procedure successfully completed.

會話2進行修改:

SQL2> UPDATE T_UNDO SET NAME = 'UPDATED NO ROW CR' WHERE ID = 1117;

1 row updated.

SQL2> COMMIT;

Commit complete.

檢查結(jié)果:

SQL3> SELECT NAME FROM T_UNDO WHERE ID = 1117;

NAME

------------------

UPDATED NO ROW CR

回到會話1檢查結(jié)果:

SQL1> PRINT :C

NAME

------------------------------------------------------------

I_EXTERNAL_TAB1$

Oracle恢復(fù)默認的讀一致性隔離級別。

雖然Oracle認為這種優(yōu)化只是針對主鍵或唯一索引等行級訪問生效,造成數(shù)據(jù)一致性破壞的可能性很小,但是建議對于一致性要求較高的行業(yè)尤其是金融相關(guān)行業(yè)還是將該特性關(guān)閉,避免因此造成的一致性問題。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI