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

2021-06-22 05:51:58 字數 3660 閱讀 1833

在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之間進行比較,哪個更大。如果資料塊頭部的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的頭部記錄乙個條目,該條目裡記錄了該事務相關的資訊,其中包括是否提交等),將其中既沒有提交也沒有回滾,而是在例項崩潰時被異常終止的事務全部回滾。

Oracle資料庫一致性讀的原理

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

資料讀一致性

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

MySQL 資料庫的一致性讀

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