Oracle資料庫一致性讀的原理

2021-05-23 10:12:28 字數 3465 閱讀 2601

在oracle資料庫中,undo主要有三大作用:提供一致性讀(consistent read)、回滾事務(rollback transaction)以及例項恢復(instance recovery)

一致性讀是相對於髒讀(dirty read)而言的。假設某個表t中有10000條記錄,獲取所有記錄需要15分鐘時間。當前時間為9點整,某使用者a發出一條查詢語句:select * from t,該語句在9點15分時執行完畢。當使用者a執行該sql語句到9點10分的時候,另外乙個使用者b發出了一條delete命令,將t表中的最後一條記錄刪除並提交了。那麼到9點15分時,a使用者將返回多少條記錄?

如果返回9999條記錄,則說明發生了髒讀;如果仍然返回10000條記錄,則說明發生了一致性讀。很明顯,在 9點鐘那個時間點發出查詢語句時,表t中確實有10000條記錄,只不過由於i/o的相對較慢,所以才會花15分鐘完成所有記錄的檢索。對於oracle 資料庫來說,沒有辦法實現髒讀,必須提供一致性讀,並且該一致性讀是在沒有阻塞使用者的dml的前提下實現的。

那麼undo資料是如何實現一致性讀的呢?還是針對上面的例子。使用者a在9點發出查詢語句時,伺服器程序會將9 點那個時間點上的scn號記錄下來,假設該scn號為scn9.00。那麼9點整的時刻的scn9.00一定大於等於記錄在所有資料塊頭部的itl槽中的 scn號(如果有多個itl槽,則為其中最大的那個scn號)。

注:itl

interested transaction list)

是oracle資料塊內部的乙個組成部分,用來記錄該塊所有發生的事務,乙個itl可以看作是乙個記錄,在乙個時間,可以記錄乙個事務(包括提交或者未提交事務)。當然,如果這個事務已經提交,那麼這個itl的位置就可以被反覆使用了,因為itl類似記錄,所以,有的時候也叫itl槽位。

伺服器程序在掃瞄表t的資料塊時,會把掃瞄到的資料塊頭部的itl槽中的scn號與scn9:00之間進行比較,www.linuxidc.com哪個更大。如果資料塊頭部的scn號比scn9.00要小,則說明該資料塊在9點以後沒有被更新,可以直接讀取其中的資料;否則,如果資料塊itl槽的scn號比scn9.00要大,則說明該資料塊在9點以後被更新了,該塊裡的資料已經不是9點那個時間點的資料了,於是要借助undo塊。

9點10分,b使用者更新了表t的最後一條記錄並提交(注意,在這裡,提交或者不提交並不是關鍵,只要使用者b更新了表t,使用者a就會去讀undo資料塊)。假設被更新記錄屬於n號資料塊。那麼這個時候n號資料塊頭部的itl槽的scn號就被改為scn9.10。當伺服器程序掃瞄到被更新的資料塊(也就是n號塊)時,發現其itl槽中的scn9.10大於發出查詢時的scn9.00,說明該資料塊在9點以後被更新了。於是伺服器程序到n號塊的頭部,找到scn9.10所在的itl槽。由於itl槽中記錄了對應的undo塊的位址,於是根據該位址找到undo塊,將 undo塊中的被修改前的資料取出,再結合n號塊裡的資料行,從而構建出9點10分被更新之前的那個時間點的資料塊內容,這樣的資料塊叫做cr塊(consistent read)。對於delete來說,其undo資訊就是insert,也就是說該構建出來的cr塊中就插入了被刪除的那條記錄。隨後,伺服器程序掃瞄該 cr塊,從而返回正確的10000條記錄。

讓我們繼續把問題複雜化。假設在9點10分b使用者刪除了最後一條記錄並提交以後,緊跟著9點11分,c使用者在同乙個資料塊裡(也就是n號塊)插入了2條記錄。這個時候oracle又是如何實現一致性讀的呢(假設表t的initrans為1,也就是只有乙個itl 槽)?因為我們已經知道,事務需要使用itl槽,只要該事務提交或回滾,該itl槽就能夠被重用。換句話說,該itl槽裡記錄的已經是scn9.11,而不是scn9.10了。這時,itl槽被覆蓋了,oracle的伺服器程序又怎能找回最初的資料呢?

其中的秘密就在於,oracle在記錄undo資料的時候,不僅記錄了改變前的資料,還記錄了改變前的資料所在的資料塊頭部的itl資訊。因此,9點10分b使用者刪除記錄時(位於n號塊裡,並假設該n號塊的itl資訊為[undo_block0 / scn8.50]),則oracle會將改變前的資料(也就是insert)放到undo塊(假設該undo塊位址為undo_block1)裡,同時在該undo塊裡記錄刪除前itl槽的資訊(也就是[undo_block0 / scn8.50])。刪除記錄以後,該n號塊的itl資訊變為 [undo_block1 / scn9.10];到了9點11分,c使用者又在n號塊裡插入了兩條記錄,則oracle將插入前的資料(也就是delete兩條記錄)放到undo塊(假設該undo塊的位址為undo_block2)裡,並將9點11分時的itl槽的資訊(也就是[undo_block1 / scn9.10])也記錄到該undo塊裡。插入兩條記錄以後,該n號塊的itl槽的資訊改為 [undo_block2 / scn9.11]。

那麼當執行查詢的伺服器程序掃瞄到n號塊時,發現scn9.11大於scn9.00,於是到itl槽中指定的 undo_block2處找到該undo塊。發現該undo塊裡記錄的itl資訊為[undo_block1 / scn9.10],其中的scn9.10仍然大於scn9.00,於是伺服器程序繼續根據itl中記錄的undo_block1,找到該undo塊。發現該undo塊裡記錄的itl資訊為[undo_block0 / scn8.50],這時itl裡的scn8.50小於發出查詢時的scn9.00,說明這時undo塊包含合適的undo資訊,於是伺服器程序不再找下去,而是將n號塊、undo_block2以及undo_block1的資料結合起來,構建cr塊。將當前n號的資料複製到cr塊裡,然後在cr塊裡先回退9點11分的事務,也就是在cr塊裡刪除兩條記錄,然後再回退9點10分的事務,也就是在cr塊裡插入被刪除的記錄,從而構建出9點鐘時的資料。 oracle就是這樣,以層層巢狀的方式,查詢整個undo塊的鍊錶,直到發現itl槽裡的scn號小於等於發出查詢時的那個scn號為止。正常來說,當前undo塊裡記錄的scn號要比上乙個undo塊裡記錄的scn號要小。

但是在查詢的過程中,可能會發現當前undo塊裡記錄的itl槽的scn號比上乙個undo塊裡記錄的scn號還要大。這種情況說明由於事務被提交或回滾,導致當前找到的undo塊裡的資料已經被其他事務覆蓋了,於是我們無法再找出小於等於發出查詢時的那個時間點的scn號,這時oracle就會丟擲乙個非常經典的錯誤——ora-1555,也就是snapshot too old的錯誤。

以上的描述可以用圖來描述:

回滾事務則是在執行dml以後,發出rollback命令撤銷dml所作的變化。oracle利用記錄在itl槽裡記錄的undo 塊的位址找到該undo塊,然後從中取出變化前的值,並放入資料塊中,從而對事務所作的變化進行回滾。

例項恢復則是在smon程序完成前滾並開啟資料庫以後發生。smon程序會去檢視undo segment頭部(所謂頭部就是undo segment裡的第乙個資料塊)記錄的事務表(每個事務在使用undo塊時,首先要在該undo塊所在的undo segment的頭部記錄乙個條目,該條目裡記錄了該事務相關的資訊,其中包括是否提交等),將其中既沒有提交也沒有回滾,而是在例項崩潰時被異常終止的事務全部回滾。

資料讀一致性

undo段由兩個元件組成 undo頭和undo入口。undo段的第一塊是undo頭。undo被建立時僅有undo頭被建立。保留時間表 retention table 這是自動管理undo新增的乙個元件。儲存了undo段中每乙個區最後乙個事務提交的時間。事務表 transaction table 儲存...

MySQL 資料庫的一致性讀

mysql 在不同的事務隔離級別下提供兩種讀模式 一致性讀 非加鎖 當前讀 加鎖讀 一致性讀 利用mvcc機制讀取到某個事務已經提交的資料,其實是從undo裡面獲取的資料快照 事務的概念 由一組dml語句組成,要麼全部成功,要麼全部失敗。事務的acid 事務的併發問題 事務的隔離級別 隔離級別 髒讀...

Oracle之資料庫一致性讀的原理

在oracle資料庫中,undo主要有三大作用 提供一致性讀 consistent read 回滾事務 rollback transaction 以及例項恢復 instance recovery 一致性讀是相對於髒讀 dirty read 而言的。假設某個表t中有10000條記錄,獲取所有記錄需要1...