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錯誤。