新聞中心
在數(shù)據(jù)庫系統(tǒng)中,事務(wù)是一個保證數(shù)據(jù)一致性的重要概念。事務(wù)是一組可以被視為單個邏輯實(shí)體的操作,這些操作要么全部成功要么全部失敗。為了保證數(shù)據(jù)的一致性,我們需要使用事務(wù)來對數(shù)據(jù)庫進(jìn)行操作。而在進(jìn)行事務(wù)操作時,前滾與回滾是兩個非常重要的概念。本文將詳細(xì)介紹前滾和回滾的定義、作用以及如何使用它們來實(shí)現(xiàn)數(shù)據(jù)庫操作的事務(wù)安全性。

成都創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站設(shè)計(jì)、嵐皋網(wǎng)絡(luò)推廣、微信平臺小程序開發(fā)、嵐皋網(wǎng)絡(luò)營銷、嵐皋企業(yè)策劃、嵐皋品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們最大的嘉獎;成都創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供嵐皋建站搭建服務(wù),24小時服務(wù)熱線:18980820575,官方網(wǎng)址:www.cdcxhl.com
一、前滾與回滾的定義
1.前滾
前滾(Roll Forward)指的是將數(shù)據(jù)庫系統(tǒng)恢復(fù)到某一時刻之后的狀態(tài)。回滾的操作是將數(shù)據(jù)庫恢復(fù)到之前的狀態(tài),而前滾則是將數(shù)據(jù)庫恢復(fù)到之后的狀態(tài)。當(dāng)一個事務(wù)成功提交后,數(shù)據(jù)庫系統(tǒng)會保存所有事務(wù)的操作日志。在某些情況下,我們需要將數(shù)據(jù)庫回滾到之前的狀態(tài),但也有可能需要將數(shù)據(jù)庫恢復(fù)到之后的狀態(tài)。這種情況下,我們就需要使用前滾。
2.回滾
回滾(Rollback)是將數(shù)據(jù)庫恢復(fù)到某一時刻之前的狀態(tài)。當(dāng)一個事務(wù)出現(xiàn)錯誤或者被撤銷時,我們需要將數(shù)據(jù)庫回滾到之前的狀態(tài)?;貪L操作會撤銷之前的事務(wù)操作,將數(shù)據(jù)庫恢復(fù)到之前的狀態(tài),確保數(shù)據(jù)的一致性。
二、前滾與回滾的作用
1.前滾的作用
當(dāng)出現(xiàn)數(shù)據(jù)庫故障時,我們需要使用前滾操作,將數(shù)據(jù)庫恢復(fù)到前一時刻之后的狀態(tài)。在使用前滾之前,我們需要確保數(shù)據(jù)庫的備份已經(jīng)完整,并且日志文件不會被破壞。前滾的作用不僅僅是用于數(shù)據(jù)庫故障的恢復(fù),還可以用于數(shù)據(jù)恢復(fù)、備份等方面。
2.回滾的作用
當(dāng)一個事務(wù)失敗或被撤銷時,我們需要使用回滾來保證數(shù)據(jù)的一致性?;貪L操作可以撤銷之前的事務(wù)操作,將數(shù)據(jù)庫恢復(fù)到之前的狀態(tài)?;貪L的作用不僅僅是保證數(shù)據(jù)的一致性,還可以用于并發(fā)控制、備份等方面。如果一個事務(wù)在執(zhí)行操作時遇到錯誤或者被撤銷,回滾操作可以將數(shù)據(jù)庫恢復(fù)到之前的狀態(tài),保證數(shù)據(jù)的完整性和一致性。
三、如何使用前滾和回滾實(shí)現(xiàn)數(shù)據(jù)庫操作的事務(wù)安全性
前滾和回滾可以幫助我們實(shí)現(xiàn)數(shù)據(jù)庫操作的事務(wù)安全性,確保數(shù)據(jù)的完整性和一致性。在進(jìn)行數(shù)據(jù)庫操作時,我們需要遵循以下步驟:
1.開啟事務(wù)
在進(jìn)行數(shù)據(jù)庫操作之前,我們需要開啟一個事務(wù)。在開啟事務(wù)時,數(shù)據(jù)庫系統(tǒng)會為每個事務(wù)分配一個唯一的標(biāo)識符(Transaction ID)。開啟事務(wù)后,所有的操作都將在這個事務(wù)中進(jìn)行。
2.執(zhí)行操作
在事務(wù)中,我們可以對數(shù)據(jù)庫進(jìn)行操作,包括插入、更新、刪除等操作。這些操作會被保存在日志文件中,等待提交或回滾。
3.提交或回滾事務(wù)
當(dāng)所有的操作都完成后,我們需要將事務(wù)提交或回滾。如果操作成功完成,我們可以提交事務(wù),將數(shù)據(jù)更新到數(shù)據(jù)庫中。如果操作失敗或者被撤銷,我們可以回滾事務(wù),撤銷之前的操作,將數(shù)據(jù)庫恢復(fù)到之前的狀態(tài)。
4.關(guān)閉事務(wù)
在事務(wù)提交或回滾后,我們需要關(guān)閉事務(wù)。關(guān)閉事務(wù)會釋放所有的鎖,并將之前的日志文件刪除。如果事務(wù)成功提交,所有的操作將保存在數(shù)據(jù)庫中;如果事務(wù)失敗或者被撤銷,數(shù)據(jù)庫將恢復(fù)到之前的狀態(tài)。
:
前滾和回滾是實(shí)現(xiàn)數(shù)據(jù)庫操作事務(wù)安全性的重要手段,能夠保證數(shù)據(jù)的完整性和一致性。在進(jìn)行數(shù)據(jù)庫操作時,我們需要遵循規(guī)范的步驟,開啟事務(wù)、執(zhí)行操作、提交或回滾事務(wù)、關(guān)閉事務(wù)。只有這樣,我們才能保證數(shù)據(jù)庫的操作正確、高效和安全。
成都網(wǎng)站建設(shè)公司-創(chuàng)新互聯(lián)為您提供網(wǎng)站建設(shè)、網(wǎng)站制作、網(wǎng)頁設(shè)計(jì)及定制高端網(wǎng)站建設(shè)服務(wù)!
sql運(yùn)行問題?
sql運(yùn)行問題?
數(shù)據(jù)庫運(yùn)行過程中常見的故障有3類:事物故障、系統(tǒng)故障、介質(zhì)故障。
恢復(fù)策略:
1、事物故障:
發(fā)生事務(wù)故障時,被迫中斷的事務(wù)可能已對數(shù)據(jù)庫進(jìn)行丁修改,為了消除該事務(wù)對數(shù)據(jù)庫的影響,要利用日志文件中所記載的信息,強(qiáng)行回滾該事務(wù),將數(shù)據(jù)庫恢復(fù)到修改前的初始狀態(tài)。
為此,要檢查日志文件中由這些事務(wù)所升謹(jǐn)引起的發(fā)生變化的記錄,取消這些沒有完成的事務(wù)所做的一切改變,這吵春基類恢復(fù)操作稱為事務(wù)撤銷。
2、系統(tǒng)故障:
系統(tǒng)故障的恢復(fù)要完成兩方面的工作,既要撤銷所有末完成的事務(wù),還要重做所有已提交的事務(wù),這樣才能將數(shù)據(jù)庫真正恢復(fù)到一致的狀態(tài)。
3、介質(zhì)故障:
介質(zhì)故障比事務(wù)故障和系統(tǒng)故障發(fā)生的可能性要小,但這是最嚴(yán)重的一種故障,破壞性很大,磁盤上的物理數(shù)據(jù)和日志文件可能被破壞,這需要裝入發(fā)生介質(zhì)故障前最新的后備數(shù)據(jù)庫副本,然后利用日志文件重做該副本后所運(yùn)行的所有事務(wù)。
“數(shù)據(jù)故障恢復(fù)”和“完整森肢性約束”、“并e799bee5baa6e4b893e5b19e發(fā)控制”一樣,都是數(shù)據(jù)庫數(shù)據(jù)保護(hù)機(jī)制中的一種完整性控制。所有的系統(tǒng)都免不了會發(fā)生故障,有可能是硬件失靈,有可能是軟件系統(tǒng)崩潰,也有可能是其他外界的原因,比如斷電等等。
數(shù)據(jù)庫運(yùn)行的突然中斷會使數(shù)據(jù)庫處在一個錯誤的狀態(tài),而且故障排除后沒有辦法讓系統(tǒng)精確地從斷點(diǎn)繼續(xù)執(zhí)行下去。這就要求DBMS要有一套故障后的數(shù)據(jù)恢復(fù)機(jī)構(gòu),保證數(shù)據(jù)庫能夠回復(fù)到一致的、正確地狀態(tài)去。
1、用戶進(jìn)程提交一條sql語句給服務(wù)器進(jìn)程
SELECT * FROM EMP WHERE EMPNO = 7999
2、服務(wù)器進(jìn)程從用戶進(jìn)程把信息接收到后,在PGA中就要此進(jìn)程分配所需內(nèi)存,存儲相關(guān)的信息,如在會話內(nèi)存存儲相關(guān)的登錄信息等。
3、服務(wù)器進(jìn)程把這個sql語句的字符轉(zhuǎn)化為ASCII等效數(shù)字碼,接著這個ASCII碼被傳遞給一個HASH 函數(shù),并返回一個hash 值,然后服務(wù)器進(jìn)程將到shared pool中的library cache中去查找是否存在相同的hash值,如果存在,服務(wù)器進(jìn)程將使用這條語句已在高速緩存在 SHARED POOL 的library cache 中的已分析過的執(zhí)行計(jì)劃版本來執(zhí)行。
4、如果不存在,服務(wù)器進(jìn)程將在CGA中,配合UGA內(nèi)容對sql,進(jìn)行語法分析,首先檢查語法的正確性,接著對語句中涉及的表,索引,視圖等對象進(jìn)行解析,并對照數(shù)據(jù)字典檢查這些對象的名稱以及相關(guān)結(jié)構(gòu),并根據(jù)ORACLE 選用的優(yōu)化模式以及數(shù)據(jù)字典中是否存在相應(yīng)對象的統(tǒng)計(jì)數(shù)據(jù)和是否使用了存儲大綱來生成一個執(zhí)行計(jì)劃或從存儲大綱中選用一個執(zhí)行計(jì)劃,然后再用數(shù)據(jù)字典核對此用戶對相應(yīng)對象的執(zhí)行權(quán)限,最后生成一個編譯代碼。
5、ORACLE 將這條 sql 語句的本身實(shí)際文本、HASH 值、編譯代碼、與此語名相關(guān)聯(lián)的任何統(tǒng)計(jì)數(shù)據(jù)和該語句的執(zhí)行計(jì)劃緩存在 SHARED POOL 的 library cache中。服務(wù)器進(jìn)程通過 SHARED POOL 鎖存器(shared pool latch)來申請可以向哪些共享 PL/SQL 區(qū)中緩存這此內(nèi)容,也就是說被SHARED POOL 鎖存器鎖定的 PL/SQL 區(qū)中的塊不可被覆蓋,因?yàn)檫@些塊可能被其它進(jìn)程所使用。
6、在 SQL 分析階段將用到 LIBRARY CACHE,從數(shù)據(jù)字典中核對表、視圖等結(jié)構(gòu)的時候,需要將數(shù)據(jù)字典從磁盤讀入 LIBRARY CACHE,因此,在讀入之前也要使用LIBRARY CACHE 鎖存器(library cache pin,library cache lock)來申請用于緩存數(shù)據(jù)字典。到現(xiàn)在為止,這盯慧臘個 sql 語句已經(jīng)被編譯成可執(zhí)碧桐行的代碼了,但還不知道要操作哪些數(shù)據(jù),所以服務(wù)器進(jìn)程還要為這個 sql 準(zhǔn)備預(yù)處理數(shù)據(jù)。
7、首先服務(wù)器進(jìn)程要判斷所需數(shù)據(jù)是否在 db buffer 存在,如果存在且可用,則直接獲取該數(shù)據(jù),同時根據(jù)LRU 算法增加其訪問計(jì)數(shù);如果 buffer不存在所需數(shù)據(jù),則要從數(shù)據(jù)文件上讀取首先服務(wù)器進(jìn)程將在表頭部請求 TM 鎖(保證此事務(wù)執(zhí)行過程其他用戶不能修改表的結(jié)構(gòu)),如果成功加 TM 鎖,再請求一些行級鎖(TX 鎖),如果 TM、TX 鎖都成功加鎖,那么才開始從數(shù)據(jù)文件讀數(shù)據(jù),在讀數(shù)據(jù)之前,要先為讀取的文件準(zhǔn)備好buffer 空間。服務(wù)器進(jìn)程需要掃面 LRU list 尋找 free db buffer,掃描的過程中,服務(wù)器進(jìn)程會把發(fā)現(xiàn)的所有已經(jīng)被凱滑修改過的 db buffer 注冊到 dirty list 中, 這些 dirty buffer 會通過 dbwr 的觸發(fā)條件,隨后會被寫出到數(shù)據(jù)文件,找到了足夠的空閑 buffer,就可以把請求的數(shù)據(jù)行所在的數(shù)據(jù)塊放入到 db buffer 的空閑區(qū)域或者覆蓋已經(jīng)被擠出 LRU list 的非臟數(shù)據(jù)塊緩沖區(qū),并排列在 LRU list 的頭部,也就是在數(shù)據(jù)塊放入 DB BUFFER 之前也是要先申請 db buffer 中的鎖存器,成功加鎖后,才能讀數(shù)據(jù)到 db buffer。
8、記日志現(xiàn)在數(shù)據(jù)已經(jīng)被讀入到db buffer了,現(xiàn)在服務(wù)器進(jìn)程將該語句所影響的并被讀入db buffer 中的這些行數(shù)據(jù)的 rowid 及要更新的原值和新值及 scn 等信息從 PGA 逐條的寫入 redo log buffer 中。在寫入 redo log buffer 之前也要事先請求 redo log buffer 的鎖存器,成功加鎖后才開始寫入,當(dāng)寫入達(dá)到 redo log buffer 大小的三分之一或?qū)懭肓窟_(dá)到 1M 或超過三秒后或發(fā)生檢查點(diǎn)時或者 dbwr 之前發(fā)生,都會觸發(fā)lgwr進(jìn)程把redo log buffer 的數(shù)據(jù)寫入磁盤上的 redo file 文件中(這個時候會產(chǎn)生log file sync 等待事件)已經(jīng)被寫入 redofile 的 redo log buffer 所持有的鎖存器會被釋放,并可被后來的寫入信息覆蓋,redo log buffer是循環(huán)使用的。Redo file 也是循環(huán)使用的,當(dāng)一個 redo file 寫滿后,lgwr 進(jìn)程會自動切換到下一 redo file(這個時候可能出現(xiàn) log fileswitch(checkpoint complete)等待事件)。如果是歸檔模式,歸檔進(jìn)程還要將前一個寫滿的 redo file 文件的內(nèi)容寫到歸檔日志文件中(這個時候可能出現(xiàn) log file switch(archiving needed)。
9、為事務(wù)建立回滾段在完成本事務(wù)所有相關(guān)的 redo log buffer 之后,服務(wù)器進(jìn)程開始改寫這個 db buffer的塊頭部事務(wù)列表并寫入 scn,然后 copy 包含這個塊的頭部事務(wù)列表及 scn 信息的數(shù)據(jù)副本放入回滾段中,將這時回滾段中的信息稱為數(shù)據(jù)塊的 前映 像 , 這個前映像用于以后的回滾、恢復(fù)和一致性讀。(回滾段可以存儲在專門的回滾表空間中,這個表空間由一個或多個物理文件組成,并專用于回滾表空間,回滾段也可在其它 表空間中的數(shù)據(jù)文件中開辟。
10、本事務(wù)修改數(shù)據(jù)塊 準(zhǔn)備工作都已經(jīng)做好了,現(xiàn)在可以改寫 db buffer 塊的數(shù)據(jù)內(nèi)容了,并在塊的頭部寫 入回滾段的地址。
11、放入 dirty list 如果一個行數(shù)據(jù)多次 update 而未 commit,則在回滾段中將會有多個 前映像除了之一個前映像含有scn信息外,其他每個前映像的頭部都有scn信息和前前映像回滾段地址。一個update 只對應(yīng)一個scn,然后服務(wù)器進(jìn)程將在 dirty list中建立一 條指向此db buffer 塊的指針(方便 dbwr 進(jìn)程可以找到 dirty list 的 db buffer 數(shù)據(jù)塊并寫入數(shù)據(jù)文件中)。 接著服務(wù)器進(jìn)程會從數(shù)據(jù)文件中繼續(xù)讀入第二個數(shù)據(jù)塊,重復(fù)前一數(shù)據(jù)塊的動作,數(shù)據(jù)塊的讀入、記日志、建 立回滾段、修改數(shù)據(jù)塊、放入 dirty list。當(dāng) dirty queue 的長度達(dá)到閥值(一般是 25%),服務(wù)器進(jìn)程將通知dbwr 把臟數(shù)據(jù)寫出,就是釋放 db buffer 上的鎖存器,騰出更多的 free db buffer。前面一直都是在說明oracle 一次讀一個數(shù)據(jù)塊,其實(shí) oracle 可以一次讀入多個數(shù)據(jù)塊(db_file_multiblock_read_count 來設(shè)置一 次讀入塊的個數(shù))
12、在預(yù)處理的數(shù)據(jù)已經(jīng)緩存在 db buffer 或剛剛被從數(shù)據(jù)文件讀入到 db buffer 中,就要根據(jù) sql 語句的類型來決定接下來如何操作。
(1)如果是 select 語句,則要查看 db buffer 塊的頭部是否有事務(wù),如果有事務(wù),則從回滾段中讀取數(shù)據(jù);如果沒有事務(wù),則比較 select 的 scn 和 db buffer 塊頭部的 scn,如果前者小于后者,仍然要從回滾段中讀取數(shù)據(jù);如果前者大于后者,說明這是一非臟緩存,可以直接讀取這個 db buffer 塊的中內(nèi)容。
(2)如果是 DML 操作,則即使在 db buffer 中找到一個沒有事務(wù),而且 SCN 比自己小的非臟緩存數(shù)據(jù)塊,服務(wù)器進(jìn)程仍然要到表的頭部對這條記錄申請加鎖,加鎖成功才能進(jìn)行后續(xù)動作,如果不成功,則要等待前面的進(jìn)程解鎖后才能進(jìn)行動作(這個時候阻塞是 tx 鎖阻塞)。用戶 commit 或 rollback 到現(xiàn)在為止,數(shù)據(jù)已經(jīng)在 db buffer 或數(shù)據(jù)文件中修改完成,但是否要永久寫到數(shù)文件中,要由用戶來決定 commit(保存更改到數(shù)據(jù)文件) rollback 撤銷數(shù)據(jù)的更改)。
如果用戶執(zhí)行 commit 命令:
只有當(dāng) sql 語句所影響的所有行所在的最后一個塊被讀入 db buffer 并且重做信息被寫入 redo log buffer(僅指日志緩沖區(qū),而不包括日志文件)之后,用戶才可以發(fā)去 commit 命令,commit 觸發(fā) lgwr 進(jìn)程,但不強(qiáng)制立即 dbwr來釋放所有相應(yīng) db buffer 塊的鎖(也就是no-force-at-commit,即提交不強(qiáng)制寫),也就是說有可能雖然已經(jīng) commit 了,但在隨后的一段時間內(nèi) dbwr 還在寫這條 sql 語句所涉及的數(shù)據(jù)塊。表頭部的行鎖并不在 commit 之后立即釋放,而是要等 dbwr 進(jìn)程完成之后才釋放,這就可能會出現(xiàn)一個用戶請求另一用戶已經(jīng) commit 的資源不成功的現(xiàn)象。
A、從 Commit 和 dbwr 進(jìn)程結(jié)束之間的時間很短,如果恰巧在 commit 之后,dbwr 未結(jié)束之前斷電,因?yàn)閏ommit 之后的數(shù)據(jù)已經(jīng)屬于數(shù)據(jù)文件的內(nèi)容,但這部分文件沒有完全寫入到數(shù)據(jù)文件中。所以需要前滾。由于 commit 已經(jīng)觸發(fā) lgwr,這些所有未來得及寫入數(shù)據(jù)文件的更改會在實(shí)例重啟后,由 on 進(jìn)程根據(jù)重做日 志文件來前滾,完成之前 commit 未完成的工作(即把更改寫入數(shù)據(jù)文件)。
B、如果未 commit 就斷電了,因?yàn)閿?shù)據(jù)已經(jīng)在 db buffer 更改了,沒有 commit,說明這部分?jǐn)?shù)據(jù)不屬于數(shù)據(jù)文件,由于 dbwr 之前觸發(fā) lgwr 也就是只要數(shù)據(jù)更改,(肯定要先有 log) 所有 DBWR,在數(shù)據(jù)文件上的修改都會被先一步記入重做日志文件,實(shí)例重啟后,ON 進(jìn)程再根據(jù)重做日志文件來回滾。其實(shí) on 的前滾回滾是根據(jù)檢查點(diǎn)來完成的,當(dāng)一個全部檢查點(diǎn)發(fā)生的時候,首先讓 LGWR 進(jìn)程將redo log buffer 中的所有緩沖(包含未提交的重做信息)寫入重做日志文件,然后讓 dbwr 進(jìn)程將 db buffer 已 提交的緩沖寫入數(shù)據(jù)文件(不強(qiáng)制寫未提交的)。然后更新控制文件和數(shù)據(jù)文件頭部的 SCN,表明當(dāng)前數(shù)據(jù)庫是一致的,在相鄰的兩個檢查點(diǎn)之間有很多事務(wù),有提交和未提交的。像前面的前滾回滾比較完整的說法是如下的說明:
AA、發(fā)生檢查點(diǎn)之前斷電,并且當(dāng)時有一個未提交的改變正在進(jìn)行,實(shí)例重啟之后,ON 進(jìn)程將從上一個檢查點(diǎn)開始核對這個檢查點(diǎn)之后記錄在重做日志文件中已提交的和未提交改變,因?yàn)閐bwr 之前會觸發(fā) lgwr,所以 dbwr 對數(shù)據(jù)文件的修改一定會被先記錄在重做日志文件中。因此,斷電前被DBWN 寫進(jìn)數(shù)據(jù)文件的改變將通過重做日志文件中的記錄進(jìn)行還原,叫做回滾。
BB. 如果斷電時有一個已提交,但 dbwr 動作還沒有完全完成的改變存在,因?yàn)橐呀?jīng)提交,提交會觸發(fā) lgwr進(jìn)程,所以不管 dbwr 動作是否已完成,該語句將要影響的行及其產(chǎn)生的結(jié)果一定已經(jīng)記錄在重做日志文件中了,則實(shí)例重啟后,ON 進(jìn)程根據(jù)重做日志文件進(jìn)行前滾.實(shí)例失敗后用于恢復(fù)的時間由兩個檢查點(diǎn)之間的間隔大小來決定,可以通個四個參數(shù)設(shè)置檢查點(diǎn)執(zhí)行的頻率:
Log_checkpoint_interval:
決定兩個檢查點(diǎn)之間寫入重做日志文件的系統(tǒng)物理塊(redo blocks)的大小,默認(rèn)值是 0,無限制。
log_checkpoint_timeout:
兩 個 檢 查 點(diǎn) 之 間 的 時 間 長 度(秒)默 認(rèn) 值 1800s。
fast_start_io_target:
決定了用于恢復(fù)時需要處理的塊的多少,默認(rèn)值是 0,無限制。
fast_start_mttr_target:
直接決定了用于恢復(fù)的時間的長短,默認(rèn)值是 0,無限制(ON 進(jìn)程執(zhí)行的前滾 和回滾與用戶的回滾是不同的,ON 是根據(jù)重做日志文件進(jìn)行前滾或回滾,而用戶的回滾一定是根據(jù)回滾段的內(nèi)容進(jìn)行回滾的。在這里要說一下回滾段存儲的數(shù)據(jù),假如是 delete 操作,則回滾段將會記錄整個行的數(shù)據(jù),假如是 update,則回滾段只記錄被修改了的字段的變化前的數(shù)據(jù)(前映像),也就是沒有被修改的字段是不會被記錄的,假如是insert,則回滾段只記錄插入記錄的 rowid。 這樣假如事務(wù)提交,那回滾段中簡單標(biāo)記該事務(wù)已經(jīng)提交;假如是 回退,則如果操作是 delete,回退的時候把回滾段中數(shù)據(jù)重新寫回?cái)?shù)據(jù)塊,操作如果是 update,則把變化前數(shù)據(jù)修改回去,操作如果是 insert,則根據(jù)記錄的 rowid 把該記錄刪除。
如果用戶 rollback:
則服務(wù)器進(jìn)程會根據(jù)數(shù)據(jù)文件塊和 DB BUFFER 中塊的頭部的事務(wù)列表和 SCN 以及回滾段地址找到回滾段中相應(yīng)的修改前的副本,并且用這些原值來還原當(dāng)前數(shù)據(jù)文件中已修改但未提交的改變。如果有多個前映像 服務(wù)器進(jìn)程會在一個前映像的頭部找到 前前映像 的回滾段地址,一直找到同一事務(wù)下的最早的一個前映像 為止。一旦發(fā)出了COMMIT,用戶就不能rollback,這使得 COMMIT 后 DBWR 進(jìn)程還沒有全部完成的后續(xù)動作得到了保障。到現(xiàn)在為例一個事務(wù)已經(jīng)結(jié)束了。
本篇文章會分析下一個 sql 語句在 MySQL 中的執(zhí)行流程,包括 sql 的查詢在 MySQL 內(nèi)部會怎么流轉(zhuǎn),sql 語句的更新是怎么完成的。
在分析之前我會先帶著你看看 MySQL 的基礎(chǔ)架構(gòu),知道了 MySQL 由那些組件組成以及這些組件的作用是什么,可以幫助我們理解和解決這些問題。
一 MySQL 基礎(chǔ)架構(gòu)分析
MySQL 基本架構(gòu)畢租概覽
下圖是 MySQL 的一個簡手豎兆要架構(gòu)圖,從下圖你可以很清晰的看到用戶的 SQL 語句在 MySQL 內(nèi)部是如何執(zhí)行的。
先簡單介紹一下下圖涉及的一些組件的基本作用幫助大家理解這幅圖,在 1.2 節(jié)中會詳細(xì)介紹到這些組件的作用。
連接器: 身份認(rèn)證和權(quán)限相關(guān)(登錄 MySQL 的時候)。
查詢緩存: 執(zhí)行查詢語句的時候,會先查詢緩存(MySQL 8.0 版本后移除,因?yàn)檫@個功能不太實(shí)用)。
分析器: 沒有命中緩存的話,SQL 語句就會經(jīng)過分析器,分析器說白了就是要先看你的 SQL 語句要干嘛,再檢查你的 SQL 語句語法是否正確。
優(yōu)化器: 按照 MySQL 認(rèn)為更優(yōu)的纖豎方案去執(zhí)行。
1、用戶進(jìn)程提交一條sql語句給服務(wù)器進(jìn)程
SELECT * FROM EMP WHERE EMPNO = 7999
2、服務(wù)器進(jìn)程從用戶進(jìn)程把信息接收到后,在PGA中就要此進(jìn)程分配所需內(nèi)存,存儲相關(guān)的信息,如在會話內(nèi)存存儲相關(guān)的登錄信息等。
3、服務(wù)器進(jìn)程把這個sql語句的字符轉(zhuǎn)化為ASCII等效數(shù)字碼,接著這個ASCII碼被傳遞給一個HASH 函數(shù),并返回一個hash 值,然后服務(wù)器進(jìn)程將到shared pool中的library cache中去查找是否存在相同的hash值,如果存在,服務(wù)器進(jìn)程將使用這條語句已在高速緩存在 SHARED POOL 的library cache 中的已分析過的執(zhí)行計(jì)劃版本來執(zhí)行。
4、如果不存在,服務(wù)器進(jìn)程將在CGA中,配合UGA內(nèi)容對sql,進(jìn)行語法分析,首先檢查語法的正確性,接著對語句中涉及的表,索引,視圖等對象進(jìn)行解析,并對照數(shù)據(jù)字典檢查這些對象的名稱以及相關(guān)結(jié)構(gòu),并根據(jù)ORACLE 選用的優(yōu)化模式以及數(shù)據(jù)字典中是否存在相應(yīng)對象的統(tǒng)計(jì)數(shù)據(jù)和是否使用了存儲大綱來生成一個執(zhí)行計(jì)劃或從存儲大綱中選用一個執(zhí)行計(jì)劃,然后再用數(shù)據(jù)字典核對此用戶對相應(yīng)對象的執(zhí)行權(quán)限,最后生成一個編譯代碼。
5、ORACLE 將這條 sql 語句的本身實(shí)際文本、HASH 值、編譯代碼、與此語名相關(guān)聯(lián)的任何統(tǒng)計(jì)數(shù)據(jù)和該語句的執(zhí)行計(jì)劃緩存在 SHARED POOL 的 library cache中。服務(wù)器進(jìn)程通過 SHARED POOL 鎖存器(shared pool latch)來申請可以向哪些共享 PL/SQL 區(qū)中緩存這此內(nèi)容,也就是說被SHARED POOL 鎖存器鎖定的 PL/SQL 區(qū)中的塊不可被覆蓋,因?yàn)檫@些塊可能被其它進(jìn)程所使用。
6、在 SQL 分析階段將用到 LIBRARY CACHE,從數(shù)據(jù)字典中核對表、視圖等結(jié)構(gòu)的時候,需要將數(shù)據(jù)字典從磁盤讀入 LIBRARY CACHE,因此,在讀入之前也要使用LIBRARY CACHE 鎖存器(library cache pin,library cache lock)來申請用于緩存數(shù)據(jù)字典。到現(xiàn)在為止,這盯慧臘個 sql 語句已經(jīng)被編譯成可執(zhí)碧桐行的代碼了,但還不知道要操作哪些數(shù)據(jù),所以服務(wù)器進(jìn)程還要為這個 sql 準(zhǔn)備預(yù)處理數(shù)據(jù)。
7、首先服務(wù)器進(jìn)程要判斷所需數(shù)據(jù)是否在 db buffer 存在,如果存在且可用,則直接獲取該數(shù)據(jù),同時根據(jù)LRU 算法增加其訪問計(jì)數(shù);如果 buffer不存在所需數(shù)據(jù),則要從數(shù)據(jù)文件上讀取首先服務(wù)器進(jìn)程將在表頭部請求 TM 鎖(保證此事務(wù)執(zhí)行過程其他用戶不能修改表的結(jié)構(gòu)),如果成功加 TM 鎖,再請求一些行級鎖(TX 鎖),如果 TM、TX 鎖都成功加鎖,那么才開始從數(shù)據(jù)文件讀數(shù)據(jù),在讀數(shù)據(jù)之前,要先為讀取的文件準(zhǔn)備好buffer 空間。服務(wù)器進(jìn)程需要掃面 LRU list 尋找 free db buffer,掃描的過程中,服務(wù)器進(jìn)程會把發(fā)現(xiàn)的所有已經(jīng)被凱滑修改過的 db buffer 注冊到 dirty list 中, 這些 dirty buffer 會通過 dbwr 的觸發(fā)條件,隨后會被寫出到數(shù)據(jù)文件,找到了足夠的空閑 buffer,就可以把請求的數(shù)據(jù)行所在的數(shù)據(jù)塊放入到 db buffer 的空閑區(qū)域或者覆蓋已經(jīng)被擠出 LRU list 的非臟數(shù)據(jù)塊緩沖區(qū),并排列在 LRU list 的頭部,也就是在數(shù)據(jù)塊放入 DB BUFFER 之前也是要先申請 db buffer 中的鎖存器,成功加鎖后,才能讀數(shù)據(jù)到 db buffer。
8、記日志現(xiàn)在數(shù)據(jù)已經(jīng)被讀入到db buffer了,現(xiàn)在服務(wù)器進(jìn)程將該語句所影響的并被讀入db buffer 中的這些行數(shù)據(jù)的 rowid 及要更新的原值和新值及 scn 等信息從 PGA 逐條的寫入 redo log buffer 中。在寫入 redo log buffer 之前也要事先請求 redo log buffer 的鎖存器,成功加鎖后才開始寫入,當(dāng)寫入達(dá)到 redo log buffer 大小的三分之一或?qū)懭肓窟_(dá)到 1M 或超過三秒后或發(fā)生檢查點(diǎn)時或者 dbwr 之前發(fā)生,都會觸發(fā)lgwr進(jìn)程把redo log buffer 的數(shù)據(jù)寫入磁盤上的 redo file 文件中(這個時候會產(chǎn)生log file sync 等待事件)已經(jīng)被寫入 redofile 的 redo log buffer 所持有的鎖存器會被釋放,并可被后來的寫入信息覆蓋,redo log buffer是循環(huán)使用的。Redo file 也是循環(huán)使用的,當(dāng)一個 redo file 寫滿后,lgwr 進(jìn)程會自動切換到下一 redo file(這個時候可能出現(xiàn) log fileswitch(checkpoint complete)等待事件)。如果是歸檔模式,歸檔進(jìn)程還要將前一個寫滿的 redo file 文件的內(nèi)容寫到歸檔日志文件中(這個時候可能出現(xiàn) log file switch(archiving needed)。
9、為事務(wù)建立回滾段在完成本事務(wù)所有相關(guān)的 redo log buffer 之后,服務(wù)器進(jìn)程開始改寫這個 db buffer的塊頭部事務(wù)列表并寫入 scn,然后 copy 包含這個塊的頭部事務(wù)列表及 scn 信息的數(shù)據(jù)副本放入回滾段中,將這時回滾段中的信息稱為數(shù)據(jù)塊的 前映 像 , 這個前映像用于以后的回滾、恢復(fù)和一致性讀。(回滾段可以存儲在專門的回滾表空間中,這個表空間由一個或多個物理文件組成,并專用于回滾表空間,回滾段也可在其它 表空間中的數(shù)據(jù)文件中開辟。
10、本事務(wù)修改數(shù)據(jù)塊 準(zhǔn)備工作都已經(jīng)做好了,現(xiàn)在可以改寫 db buffer 塊的數(shù)據(jù)內(nèi)容了,并在塊的頭部寫 入回滾段的地址。
11、放入 dirty list 如果一個行數(shù)據(jù)多次 update 而未 commit,則在回滾段中將會有多個 前映像除了之一個前映像含有scn信息外,其他每個前映像的頭部都有scn信息和前前映像回滾段地址。一個update 只對應(yīng)一個scn,然后服務(wù)器進(jìn)程將在 dirty list中建立一 條指向此db buffer 塊的指針(方便 dbwr 進(jìn)程可以找到 dirty list 的 db buffer 數(shù)據(jù)塊并寫入數(shù)據(jù)文件中)。 接著服務(wù)器進(jìn)程會從數(shù)據(jù)文件中繼續(xù)讀入第二個數(shù)據(jù)塊,重復(fù)前一數(shù)據(jù)塊的動作,數(shù)據(jù)塊的讀入、記日志、建 立回滾段、修改數(shù)據(jù)塊、放入 dirty list。當(dāng) dirty queue 的長度達(dá)到閥值(一般是 25%),服務(wù)器進(jìn)程將通知dbwr 把臟數(shù)據(jù)寫出,就是釋放 db buffer 上的鎖存器,騰出更多的 free db buffer。前面一直都是在說明oracle 一次讀一個數(shù)據(jù)塊,其實(shí) oracle 可以一次讀入多個數(shù)據(jù)塊(db_file_multiblock_read_count 來設(shè)置一 次讀入塊的個數(shù))
12、在預(yù)處理的數(shù)據(jù)已經(jīng)緩存在 db buffer 或剛剛被從數(shù)據(jù)文件讀入到 db buffer 中,就要根據(jù) sql 語句的類型來決定接下來如何操作。
(1)如果是 select 語句,則要查看 db buffer 塊的頭部是否有事務(wù),如果有事務(wù),則從回滾段中讀取數(shù)據(jù);如果沒有事務(wù),則比較 select 的 scn 和 db buffer 塊頭部的 scn,如果前者小于后者,仍然要從回滾段中讀取數(shù)據(jù);如果前者大于后者,說明這是一非臟緩存,可以直接讀取這個 db buffer 塊的中內(nèi)容。
(2)如果是 DML 操作,則即使在 db buffer 中找到一個沒有事務(wù),而且 SCN 比自己小的非臟緩存數(shù)據(jù)塊,服務(wù)器進(jìn)程仍然要到表的頭部對這條記錄申請加鎖,加鎖成功才能進(jìn)行后續(xù)動作,如果不成功,則要等待前面的進(jìn)程解鎖后才能進(jìn)行動作(這個時候阻塞是 tx 鎖阻塞)。用戶 commit 或 rollback 到現(xiàn)在為止,數(shù)據(jù)已經(jīng)在 db buffer 或數(shù)據(jù)文件中修改完成,但是否要永久寫到數(shù)文件中,要由用戶來決定 commit(保存更改到數(shù)據(jù)文件) rollback 撤銷數(shù)據(jù)的更改)。
如果用戶執(zhí)行 commit 命令:
只有當(dāng) sql 語句所影響的所有行所在的最后一個塊被讀入 db buffer 并且重做信息被寫入 redo log buffer(僅指日志緩沖區(qū),而不包括日志文件)之后,用戶才可以發(fā)去 commit 命令,commit 觸發(fā) lgwr 進(jìn)程,但不強(qiáng)制立即 dbwr來釋放所有相應(yīng) db buffer 塊的鎖(也就是no-force-at-commit,即提交不強(qiáng)制寫),也就是說有可能雖然已經(jīng) commit 了,但在隨后的一段時間內(nèi) dbwr 還在寫這條 sql 語句所涉及的數(shù)據(jù)塊。表頭部的行鎖并不在 commit 之后立即釋放,而是要等 dbwr 進(jìn)程完成之后才釋放,這就可能會出現(xiàn)一個用戶請求另一用戶已經(jīng) commit 的資源不成功的現(xiàn)象。
A、從 Commit 和 dbwr 進(jìn)程結(jié)束之間的時間很短,如果恰巧在 commit 之后,dbwr 未結(jié)束之前斷電,因?yàn)閏ommit 之后的數(shù)據(jù)已經(jīng)屬于數(shù)據(jù)文件的內(nèi)容,但這部分文件沒有完全寫入到數(shù)據(jù)文件中。所以需要前滾。由于 commit 已經(jīng)觸發(fā) lgwr,這些所有未來得及寫入數(shù)據(jù)文件的更改會在實(shí)例重啟后,由 on 進(jìn)程根據(jù)重做日 志文件來前滾,完成之前 commit 未完成的工作(即把更改寫入數(shù)據(jù)文件)。
B、如果未 commit 就斷電了,因?yàn)閿?shù)據(jù)已經(jīng)在 db buffer 更改了,沒有 commit,說明這部分?jǐn)?shù)據(jù)不屬于數(shù)據(jù)文件,由于 dbwr 之前觸發(fā) lgwr 也就是只要數(shù)據(jù)更改,(肯定要先有 log) 所有 DBWR,在數(shù)據(jù)文件上的修改都會被先一步記入重做日志文件,實(shí)例重啟后,ON 進(jìn)程再根據(jù)重做日志文件來回滾。其實(shí) on 的前滾回滾是根據(jù)檢查點(diǎn)來完成的,當(dāng)一個全部檢查點(diǎn)發(fā)生的時候,首先讓 LGWR 進(jìn)程將redo log buffer 中的所有緩沖(包含未提交的重做信息)寫入重做日志文件,然后讓 dbwr 進(jìn)程將 db buffer 已 提交的緩沖寫入數(shù)據(jù)文件(不強(qiáng)制寫未提交的)。然后更新控制文件和數(shù)據(jù)文件頭部的 SCN,表明當(dāng)前數(shù)據(jù)庫是一致的,在相鄰的兩個檢查點(diǎn)之間有很多事務(wù),有提交和未提交的。像前面的前滾回滾比較完整的說法是如下的說明:
AA、發(fā)生檢查點(diǎn)之前斷電,并且當(dāng)時有一個未提交的改變正在進(jìn)行,實(shí)例重啟之后,ON 進(jìn)程將從上一個檢查點(diǎn)開始核對這個檢查點(diǎn)之后記錄在重做日志文件中已提交的和未提交改變,因?yàn)閐bwr 之前會觸發(fā) lgwr,所以 dbwr 對數(shù)據(jù)文件的修改一定會被先記錄在重做日志文件中。因此,斷電前被DBWN 寫進(jìn)數(shù)據(jù)文件的改變將通過重做日志文件中的記錄進(jìn)行還原,叫做回滾。
BB. 如果斷電時有一個已提交,但 dbwr 動作還沒有完全完成的改變存在,因?yàn)橐呀?jīng)提交,提交會觸發(fā) lgwr進(jìn)程,所以不管 dbwr 動作是否已完成,該語句將要影響的行及其產(chǎn)生的結(jié)果一定已經(jīng)記錄在重做日志文件中了,則實(shí)例重啟后,ON 進(jìn)程根據(jù)重做日志文件進(jìn)行前滾.實(shí)例失敗后用于恢復(fù)的時間由兩個檢查點(diǎn)之間的間隔大小來決定,可以通個四個參數(shù)設(shè)置檢查點(diǎn)執(zhí)行的頻率:
Log_checkpoint_interval:
決定兩個檢查點(diǎn)之間寫入重做日志文件的系統(tǒng)物理塊(redo blocks)的大小,默認(rèn)值是 0,無限制。
log_checkpoint_timeout:
兩 個 檢 查 點(diǎn) 之 間 的 時 間 長 度(秒)默 認(rèn) 值 1800s。
fast_start_io_target:
決定了用于恢復(fù)時需要處理的塊的多少,默認(rèn)值是 0,無限制。
fast_start_mttr_target:
直接決定了用于恢復(fù)的時間的長短,默認(rèn)值是 0,無限制(ON 進(jìn)程執(zhí)行的前滾 和回滾與用戶的回滾是不同的,ON 是根據(jù)重做日志文件進(jìn)行前滾或回滾,而用戶的回滾一定是根據(jù)回滾段的內(nèi)容進(jìn)行回滾的。在這里要說一下回滾段存儲的數(shù)據(jù),假如是 delete 操作,則回滾段將會記錄整個行的數(shù)據(jù),假如是 update,則回滾段只記錄被修改了的字段的變化前的數(shù)據(jù)(前映像),也就是沒有被修改的字段是不會被記錄的,假如是insert,則回滾段只記錄插入記錄的 rowid。 這樣假如事務(wù)提交,那回滾段中簡單標(biāo)記該事務(wù)已經(jīng)提交;假如是 回退,則如果操作是 delete,回退的時候把回滾段中數(shù)據(jù)重新寫回?cái)?shù)據(jù)塊,操作如果是 update,則把變化前數(shù)據(jù)修改回去,操作如果是 insert,則根據(jù)記錄的 rowid 把該記錄刪除。
如果用戶 rollback:
則服務(wù)器進(jìn)程會根據(jù)數(shù)據(jù)文件塊和 DB BUFFER 中塊的頭部的事務(wù)列表和 SCN 以及回滾段地址找到回滾段中相應(yīng)的修改前的副本,并且用這些原值來還原當(dāng)前數(shù)據(jù)文件中已修改但未提交的改變。如果有多個前映像 服務(wù)器進(jìn)程會在一個前映像的頭部找到 前前映像 的回滾段地址,一直找到同一事務(wù)下的最早的一個前映像 為止。一旦發(fā)出了COMMIT,用戶就不能rollback,這使得 COMMIT 后 DBWR 進(jìn)程還沒有全部完成的后續(xù)動作得到了保障。到現(xiàn)在為例一個事務(wù)已經(jīng)結(jié)束了。
關(guān)于數(shù)據(jù)庫前滾 回滾的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。
香港云服務(wù)器機(jī)房,創(chuàng)新互聯(lián)(www.cdcxhl.com)專業(yè)云服務(wù)器廠商,回大陸優(yōu)化帶寬,安全/穩(wěn)定/低延遲.創(chuàng)新互聯(lián)助力企業(yè)出海業(yè)務(wù),提供一站式解決方案。香港服務(wù)器-免備案低延遲-雙向CN2+BGP極速互訪!
分享文章:數(shù)據(jù)庫操作必備技能:前滾與回滾詳解(數(shù)據(jù)庫前滾回滾)
分享路徑:http://fisionsoft.com.cn/article/cohdhgj.html


咨詢
建站咨詢
