29、undo_2_1(事務槽、延遲塊清除、構造CR塊、ora-01555)

一只c小凶许發表於2024-11-22

事務槽(不同於事務表裡面的槽位(這個事務槽在資料塊的頭部))


圖解:
一個事務開始,要做的事情:
第一,事務表裡面找槽位(undo段的段頭塊裡有事務表,事務表有槽位,每一個槽位記錄一個事務);
事務表裡的槽位裡記錄的資訊有:
1、xid(事務ID):(undo段的段號,段裡面的第幾個槽(槽號),覆蓋數);
2、cflag(commit flag):提交標誌;
3、SCN號(提交SCN號);
4、dba(資料塊地址):指向事務使用的最後一個塊;

第二,做DML操作,要修改某個資料塊裡面的一個資料行,在修改資料行以前,oracle會做一件事情:對於普通資料塊來講,在資料塊的頭部預設有兩個事務槽,最多可以有255個。
事務槽裡面含有的資訊:
1、xid;
2、uba(undo block adress):undo塊地址;
3、提交標誌;
4、SCN號;

事務開始了,要修改資料塊,必須先獲取到資料塊裡面的事務槽;假設0號事務槽裡面沒有未提交事務(就是沒有active事務槽的時候),這個事務槽就可以被覆蓋;要修改多行資料的時候,用一個事務槽就可以了;然後將事務資訊寫入事務槽;

現在要修改資料行了,會在資料行的頭部寫入一個數字(比如:0(事務槽的槽號)),表示在資料行上加了一個鎖(也就是說這個資料行正在被0號事務槽裡面所對應的事務修改),然後指向0號事務槽,然後事務槽裡面的xid指向這個事務的事務源;同一個事務可以修改資料塊裡面的多行資料(這裡修改了3行資料),都指向了同一個事務;另外還修改了另外一個資料塊,然後也指向同一個事務源(一個事務可以同時修改多個資料塊);

關於事務的提交標誌,在很多地方有,比如:在每一個資料塊的事務槽裡面有提交標誌,在事務表裡面的槽位裡面有提交標誌,並且都有SCN號;

現在描述一個場景:
假設,一個事務修改了100W個塊,然後要commit提交;要提交的時候oracle會做幾件事情:
1、清鎖:需要把100W個塊裡面的每個資料行前面的鎖標記清除了;
2、清理資料塊的事務槽:就是把事務槽裡面的資訊清除了;
這兩個清理的會很慢(非常消耗資源),因為涉及到100W個塊;
3、清理undo段裡面的事務表;

這個清理的比較快(不消耗資源);
使用者提交的時候,希望馬上提交成功,但是提交的時候第一和第二件事情很慢,很消耗資源,所以oracle在提交的時候,只清除事務表裡面的資訊(這個是必須要做的事情),記下提交時候的SCN號,然後第一和第二件事情oracle也會盡量的去做一些(比如,現在是100W個塊,然後呢,只修改1000個塊),意思意思;
事務表裡面的清了,資料塊裡面的事務槽還沒有清理完,然後oracle下次再訪問塊的時候,假設讀到之前修改的一行資料時,發現上面有鎖標記,表示上面有活動的事務,然後找事務槽裡面的事務,顯示事務沒有提交,這時候再到事務表裡面找對應的事務,但是已經顯示提交了,oracle就知道當時提交的時候,資料塊裡的事務槽沒清理,所以oracle下次再訪問塊的時候就順便清了,把鎖標記清了,事務槽裡面的資訊清了,清理完之後就接著訪問

快速提交

所以,只要事務表裡面的資訊清了,資料塊裡面的資訊有沒有清沒關係;oracle裡,在提交的時候,把事務表裡面的資訊清了,其他的地方意思意思的清理一下,這種提交機制叫做:快速提交

延遲塊清除

然後呢,再讀到那個塊的時候,順便清了,然後修改資料塊,修改資料塊就要修改鎖標記,也要修改事務槽裡面的提交標記(修改為未提交),還有修改SCN號(改為null),修改的時候就會產生redo,這種情況叫做:延遲塊清除

有這麼一種情況:就是事務提交的時候,提交了以後,因為資料庫的壓力比較大,只清除了事務表裡面的資訊,其他的都沒有清除;假設這時候,事務表裡面的槽位是:5號段,2號槽位,第100次被覆蓋,因為這個事務提交了,這時候就被覆蓋了,就變成了:5號段,2號槽位,第101次被覆蓋;然後資料塊裡面的事務槽因為沒有清除,記錄的還是:5號段,2號槽位,第100次被覆蓋,這時候就去找,但是在undo事務表裡面已經是:5號段,2號槽位,第101次被覆蓋了,oracle就認為已經提交了,這時候接著就清除鎖和事務槽,更改SCN號,更改提交狀態,

快速提交和延遲塊清除,都是為了提高提交的速度

構造CR塊(consistent read)(讀一致性)


圖解:
一個undo段,假設裡面有三個事務(T1、T2、T3),然後修改資料塊裡面的一行資料,修改資料之前,要把修改之前的資料放到undo裡面事務T1對應的undo塊裡,修改前的值是1,修改後的值是2;

然後,select要訪問這個資料塊的這個資料行,oracle訪問的時候,發現這個塊上有事務槽,顯示事務沒有提交,然後oracle就根據xid找undue段裡面的事務表對應的事務,看看這個事務是否已經提交,然後發現這個事務確實沒有提交;

假設現在對於sp1來說,要讀這個資料塊,但是在這個資料塊上sp2在修改並且還沒有提交,這時候sp1是不能讀未提交事務的,也就是說,對於任何的會話,任何的server process都是不能讀未提交事務的;

這時候oracle會在buffer cache裡面再找一個空塊,把sp1正在修改的資料塊裡面所有行的資料取出來放到這個空塊裡,因為裡面有一行有未提交事務,它就找事務槽的uba地址,然後找到undo資料,把修改前的資料取出來,還原事務修改前的值,再放到空塊裡面去,這就構造了一個塊出來,這個塊叫做:構造CR塊

所以在oracle資料庫裡面,DML不阻塞select,也就是:修改不阻塞讀;就是當oracle訪問資料行的時候,去事務槽和undo事務表確認事務是否提交,未提交會在buffer中找到一個空塊,找到undo中的源資料,構建一個CR,所以修改資料行的時候不會堵塞讀。只讀已提交事務,不讀未提交事務。

崩潰恢復


圖解:
資料庫正在正常的執行著,一個undo段,裡面有三兩個事務(T1、T2、T3),T1對應修改三個塊,T2修改了兩個塊;T1未提交,T2提交了;

這時候資料庫突然崩了,資料庫重新啟動,重啟以後呢,select要讀T2事務對應的一個塊,這個塊是延遲塊清除,然後顯示T2這個事務沒有提交,,然後去undo事務表的槽位發現事務已經提交,oracle知道該事務已經提交,馬上清除鎖和事務槽,更改SCN號,更改提交狀態,然後直接讀這個塊;

讀完以後,接著要讀T1事務對應的一個塊,發現T1事務之前沒有提交,然後訪問undo段的事務表裡面的事務槽發現未提交,但是T1所對應的那個會話已經斷開了,不存在了,因為資料庫崩了,會話也都斷開了,T1就不可能提交和回滾了,這時候select會幫助T1做一下回滾,強求回滾,然後T1這個事務就沒了;

所以資料庫突然崩了,資料庫重新啟動,在資料庫突然崩的那一瞬間有很多的未提交事務,重啟以後呢,這些事務不可能回來了,也不可能再提交,再回滾了,但是undo裡面有undo資料在,這時候,select誰讀到相應塊的時候,就會把這個事務往前回滾一下,讀的時候回滾一下,作為恢復的一個操作

訪問以往事務修改過的資料塊的資料


圖解:
一個undo段,裡面有一個事務T1,修改了一個資料塊,然後T1提交了,還有一個事務T2,T2也要修改同樣的資料塊,因為事務槽所對應的事務T1已經提交,事務T2就可以覆蓋事務T1在資料塊中所對應的事務槽,要覆蓋事務槽就要修改事務槽中的資料,就會產生undo,對於資料塊來講,修改任何資料都會產生undo;因為T2要覆蓋事務槽就要修改資料,就要把事務槽裡面的資料拿出來放到undo段的一個塊裡,然後新的事務槽的xid指向事務T2了,這時候T2也提交了;

我們就來找,找這個資料塊資料修改的歷史,對於這個資料塊來說,被事務T1修改過,也被事務T2修改過,現在我們能看到事務T2修改後的資料,這時候,我們也可以看到事務T1修改過的資料,從T2往前推就可以找到T1修改前的資料;有T3,T4.....也是如此

假設資料庫建立以後undo表空間足夠大,沒有邊界的大;對於資料塊來講,資料庫可以找到所有的資料塊的修改資訊,就是一個資料塊被多個事務修改了

如果現在將undo returntion設定為:24小時,然後將表空間設定為:gurantee屬性;也就是這個表空間一直儲存24個小時的資料,那麼可以訪問24小時之內表的每個時刻的資料;

select * from t; 訪問現在的資料;
select * from t as time of 13:30:01;訪問13:30:01時修改過的所有資料塊的資料

ORA-01555錯誤


圖解:
比如,現在oracle有一個t表,t表比較大,下面有很多很多塊;在8:50訪問t表,在8:55的時候有一個事務修改了一個資料塊,資料塊裡面的SCN被修改為8:55,然後提交了;

假設9:00的時候讀到最後一個資料塊的最後一行,但是8:55刪除了一行資料;假設t表有一萬行,8:00的時候開始讀,到9:00的時候會讀到多少行資料?
讀取了一萬行;

因為對於select來講,讀到最後一行資料時,這行資料對應的事務已經提交了,按道理,這一行上沒有未提交事務,刪除資料已經提交了,不能被讀,但是還是讀到了10000行資料。oracle在讀select開始的時候,記住當時時間點所有資料塊的SCN號:850,oracle做了一個假設,並且假設成立:就是將要訪問的表在8:50這個時刻,所對應的所有資料塊的事務槽,它們的SCN號都應該小於等於900,突然讓時間停止,將所有資料塊讀一遍。當讀到最後一個塊的時候,發現SCN號是:855,然後找最後一條資料之前的資料,透過undo構建CR塊,找出該行資料SCN號為:850時的資料;

在8:57的時候,8:55的undo塊被覆蓋了。導致8:50的select讀資料的時候,去undo塊中找,發現資料沒了,無法構建CR塊,oracle就會報出ora-01555錯誤,報出ora-01555錯誤的因為:snapshot too old(快照太舊)或者select讀取的時間過長

導致ora-01555錯誤的原因:
可能是snapshot too old(快照太舊)或者select的執行時間太長,還有就是undo空間壓力很大,undo資料被快速覆蓋;該錯誤,會指定哪個SQL引起的問題

select訪問的是生產表,觸發ora-01555這個錯誤,原因可能是undo空間太小;

select訪問的是字典表,oracle做ddl的時候,向系統表空間的資料字典做insert操作,會將修改資訊寫到系統表空間的undo段中專門給字典表做回滾;

所以訪問的是字典表的時候,發生01555錯誤是系統表空間的undo段引起的,很可能是一個bug,mos中查詢。

回滾

回滾的本質:
對某個表做delete的時候,忘了加where條件,然後刪;假設這個表有1000萬行,刪除了30分鐘還沒結束,這時候這個過程產生了大量的undo資料;

然後後悔了,想結束這個事務,就是將視窗叉掉,然後剛才做的這個事務需要回滾;回滾的時候,oracle會讀undo,將這30分鐘刪除的資料找出來,然後對錶進行insert操作,這時候也會產生redo,但是這個可能需要花費60分鐘;當然,如果不叉掉接著執行,可能再過2分鐘就執行完了,或者回滾需要90分鐘,這些都是有可能的;

因為誤刪了,必須回滾。假設操作是正常操作,表已經刪除了20分鐘,還未結束。很長時間不能訪問這個表,因為在表上加了很多鎖。想結束這個事務,這個事務就需要回滾,回滾可能需要更長的時間。在資料庫中用SQL可以查某個事務還有多少時間結束。

幾個操作所佔用的undo和redo的情況:
insert操作主要消耗redo資源; delete操作主要消耗undo資源; update操作消耗redo和undo操作;

注意:對於oracle來說,DML主要消耗redo和undo資源

這兩個資源需要注意:
假設有一個表T,有一個id列,現在想將表中的id列刪除了:
做的第一件事,將T表用排他鎖鎖住,任何人不能訪問這個表;

第二,將表裡所有的塊,假設有100萬個塊,裡面id列的資料,全部刪除,所有的塊都是掃描一遍,刪除一遍;

第三,執行的話,T表中所有的資料都要進行操作,產生大量的redo和undo;

第四,執行的時間太長;假如執行時間很長,生產扛不住了,叉掉視窗,這個事務就需要回滾,這時候可能需要更長的時間,這個表只能廢掉了;

但是在表裡面加一個不加預設值的列,一下子就完成了。只會在資料字典裡面新增列。但是新增一個列的時候,設定了預設值,這個操作和刪除一個列資料庫進行的操作一樣,同樣危險。

相關文章