Commit和dbwr沒有任何關係、物理讀產生邏輯讀、快照過舊的理解

lusklusklusk發表於2017-09-12
The COMMIT statement ends the current transaction, making its changes permanent and visible to other users.

commit僅僅是觸發lgwr把日誌緩衝資料寫入線上重做日誌並且把提交了資料的事務的scn將記錄在控制檔案中,注意此時的資料檔案中的scn沒有變化,COMMIT不會觸發任何的checkpoint

The DBWn process writes dirty buffers to disk under the following conditions:
--When a server process cannot find a clean reusable buffer after scanning a threshold number of buffers, it signals DBWn to write. DBWn writes dirty buffers to disk asynchronously if possible while performing other processing.
--DBWn periodically writes buffers to advance the checkpoint
在以下條件下,DBWn程式將髒緩衝區寫入磁碟:
--當伺服器程式在掃描閾值數量的緩衝區之後找不到乾淨的可重用緩衝區時,它會將DBWn發訊號寫入。 執行其他處理時,如果可能,DBWn將非同步緩衝區寫入磁碟。
--定期寫入緩衝區以推進檢查點

所以Commit和Dbwr沒有任何關係
一些資料塊,產生它們的事務還未提交,但是它們已經被DBWn寫回到資料檔案中了,當然也提前寫入redo中了(dbwr寫前協議,即某個資料還沒有寫入redo就要發生dbwr則必須等待lgwr將資料寫入redo,寫redo不止包含commit一種條件。)DBWn清理髒資料塊從來就同事務commit與否沒有任何關係。只要記憶體沒有有空閒,就會把這些髒塊給flush到資料檔案內。這些未提交的事務但被寫入資料檔案的資料,即便是已經在你的資料檔案了。事務恢復階段將按需把這些資料回滾掉事務恢復後已經是垃圾資料了(當事務修改資料時,會先在回滾段中儲存一份修改前資料和事務開始的SCN,如果事務沒有commit,則標記段頭部為ITL,如果commit,則把commit時刻的SCN寫入資料塊中,回滾的時候根據段是否commit來定位,如果沒有commit就直接找到undo中這個會話最初的scn和前映象直接回滾,不會一個個資料塊去undo,否則10G都已經寫入資料檔案那回滾得多久啊)

實驗過,10G容量級別的大量insert操作了30分鐘,但是不commit,會發現redo log不停的切換產生歸檔日誌,且datafile不停增加。再直接shutdown abort,startup的時候發現很快,不需要30分鐘。前滾回滾過程應該是這樣的:資料庫記錄了最新的SCN、增量checkpoint的SCN、redo log的最大SCN,透過增量checkpoint的SCN開始應用redo log直到redo log的最大SCN乃至最新的SCN,這樣就完成了前滾,在回滾的時候直接讀取undo中這個會話最初的scn和前映象直接回滾,不會一個個資料塊去undo,否則10G都已經寫入資料檔案那回滾得多久啊





讀取一個資料塊,則這個資料塊要麼直接來自datafile,要麼來自Buffer Cache,如果來自datafile,則也要讀取到SGA中的Buffer Cache中,也就是一次物理讀必然產生一個邏輯讀的意思,資料塊上都會有最後一次修改資料塊後commit的SCN
如果一個事務需要修改資料塊中資料,會先在回滾段中儲存一份修改前資料和事務開始的SCN,然後再更新Buffer Cache中的資料塊的資料,如果沒有commit則標記段頭部的TIL,如果已經commit則把commit後的SCN更新到資料塊上。當其他程式讀取資料塊時,會先比較資料塊上的SCN和自己的SCN。如果資料塊上的SCN 小於等於程式本身的SCN,則直接讀取資料塊上的資料;如果資料塊上的SCN大於程式本身的SCN,則會從回滾段中找出修改前的資料塊讀取資料。
Oracle的一致性讀的理解:一個語句在讀取資料快時,如果發現這個資料塊是在它讀取的過程中被修改的(資料塊上的SCN 大於等於讀取程式本身的SCN),就不直接從資料塊上讀取資料,而是從相應的undo中讀取資料。這就保證了最終結果應該是讀操作開始時的那一時刻的快照 (snapshot),而不會受到讀期間其他事務的影響。當然如果放在undo裡面的資料被覆蓋了,就會報錯ORA-01555:快照過舊


瞭解Oracle在什麼情況下會產生ORA-01555:快照過舊錯誤
假設有一張6000萬行資料的testdb表,預計testdb全表掃描1次需要2個小時,參考過程如下:
1、在1點鐘,使用者A發出了select * from testdb;此時不管將來testdb怎麼變化,正確的結果應該是使用者A會看到在1點鐘這個時刻的內容。
2、在1點30分,使用者B執行了update命令,更新了testdb表中的第4100萬行的這條記錄,這時,使用者A的全表掃描還沒有到達第4100萬條。毫無疑問,這個時候,第4100萬行的這條記錄是被寫入了回滾段,假設是回滾段UNDOTS1,如果使用者A的全表掃描到達了第4100萬行,是應該會正確的從回滾段UNDOTS1中讀取出1點鐘時刻的內容的。
3、這時,使用者B將他剛才做的操作提交了,但是這時,系統仍然可以給使用者A提供正確的資料,因為那第4100萬行記錄的內容仍然還在回滾段UNDOTS1裡,系統可以根據SCN到回滾段裡找到正確的資料,但要注意到,這時記錄在UNDOTS1裡的第4100萬行記錄已經發生了重大的改變:就是第4100萬行在回滾段UNDOTS1裡的資料有可能隨時被覆蓋掉,因為這條記錄已經被提交了!
4、由於使用者A的查詢時間漫長,而業務在一直不斷的進行,UNDOTS1回滾段在被多個不同的transaction使用著,這個回滾段裡的extent迴圈到了第4100萬行資料所在的extent,由於這條記錄已經被標記提交了,所以這個extent是可以被其他transaction覆蓋掉的!
5、到了1點45分,使用者A的查詢終於到了第4100萬行,而這時已經出現了第4條說的情況,需要到回滾段UNDOTS1去找資料,但是已經被覆蓋掉了,這時就出現了ORA-01555錯誤。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30126024/viewspace-2144884/,如需轉載,請註明出處,否則將追究法律責任。

相關文章