数据库杂谈(九)——事务管理
文章目錄
- 9 事務管理
- 9.1 恢復機制
- 9.2 事務和日志
- 9.2.1 事務
- 9.2.2 運行記錄的結構
- 9.2.2.1 活動事務表
- 9.2.2.2 提交事務表
- 9.2.2.3 日志
- 9.2.3 提交規則和先記后寫規則
- 9.2.3.1 提交規則
- 9.2.3.2 先記后寫規則
- 9.3 更新策略以及故障后的恢復
9 事務管理
9.1 恢復機制
數據對一個單位是非常重要的。也就是說,數據庫的失效常常導致一個單位的癱瘓。為此,對數據庫出現的故障有備選的方案去處理是非常重要的。
數據庫出現故障時處理的重點就突出兩個字:防、治
數據庫出現故障的時候,如果是防,我們就盡可能提高系統的可靠性;如果防不住我們就治,在數據庫系統發生故障后,把數據庫恢復到之前的狀態。一般來說,我們做第一手的準備不足以保證數據庫數據的安全,比如突然停電,這種東西誰都防不住,所以通常在第一手準備的情況下,我們會去做第二手準備,而第二手準備,就是我們討論的重點:恢復技術。
數據的恢復機制必須要求以下兩點:
一、數據冗余
硬件和軟件出現故障,就會導致運行出現差錯,也就會導致數據庫失效。故障是前因,差錯是現象,失效是后果。為了恢復失效后果,我們必須留有后備的副本以備不時之需,這樣來看的話,備份就會導致數據冗余是必需的。
這里的冗余和數據庫設計的冗余不一樣,那里的冗余指的是數據庫結構為了追求三范式,而有些地方不合理造成的冗余;而我們上面說的冗余是留有數據做備份。
二、自動檢測故障
要能夠檢測出故障,不能出現故障了還沒有停下來,稀里糊涂的繼續操作下去。
說完數據恢復必要的元素,我們來講講恢復技術有哪些。大體來說一般歸為以下三類。
單純以后備副本為基礎的恢復技術(周期性轉儲)
這個是從文件系統繼承過來的恢復技術。這里要繼續講下去又得講一下磁帶了。
磁帶就相當于小時候的錄音帶,你甚至可以理解為他是讀卡器,他是一種可以脫機的存儲設備,但是容易損壞。
所以使用以后備副本為基礎的恢復技術時,我們會把磁盤上的數據庫周期性轉儲到磁帶上,當然你也可以刻在光盤上。由于磁帶可以脫機存放,可以不受系統故障的影響,所以存在磁帶的那部分數據庫副本我們叫后備副本。
那你可以試想,數據庫如此龐大,假如真的讓你那個類似于U盤一樣的東西去拷貝,拷貝一次需要多久?而且在拷貝過程中數據庫不能改變,相當于凍結了數據庫,暫停其運作;所以在取后備副本的時候由于時間問題,我們不能頻繁的取,一般周末夜間數據庫空閑的時刻才會去操作。
但是這樣一來你就會發現一個問題:假如我取后備副本的周期過長,那就會導致假如在上一次取后備副本到數據庫突然故障這段期間我做了大量的更新的話,那么就會導致這些操作全部白干。
所以,我們想到了提高這個恢復技術的辦法,也就是增量存儲。這實際上是周期性轉儲方法的變種。一般來說,數據庫不會改動整個結構,只會改動其中一小部分,我們可以把那一小部分所對應的物理塊拿出來高頻(也就是周期很短)地轉儲,其他的物理塊可以很久才轉儲一次,這樣的話,一旦發生故障,馬上就能恢復最近更新的那一部分的物理塊,損失度大幅下降。
| 每次轉儲全部數據庫 | 只轉儲上次轉儲更新過的數據 |
對于海量存儲和增量存儲來說各有好壞:
- 從恢復角度看,使用海量轉儲得到的后備副本進行恢復往往更方便
- 如果數據庫很大,事務處理又十分頻繁,則增量轉儲更有效
由于這種方法實現起來很簡單,所以在文件系統使用得很多,但是在數據庫系統中只用于小型的和不重要的數據庫系統。
以后備副本和運行記錄(log或者journal)為基礎的恢復技術
這種技術是企業里面用的最多的技術。
在SQLserver中,我們能看到建數據庫的時候我們還會建一個日志文件log,這里就派生用場了。日志實際上就是一個流水賬,我們也叫他是一個運行記錄,記錄你所有操作的流水;其中運行記錄里面記了兩樣東西,一種是前像,一種是后像。
前像(也就是老的數據,老值)(Before image,簡稱BI)
如果數據庫被事務做了一次更新,那么我們就可以利用還沒更新前數據庫的數據涉及的物理塊來恢復更新前的狀態,也就是撤銷更新,這種操作我們在恢復技術里叫做撤銷(英文簡稱undo)。
后像(也就是新的數據,新值)(After image,簡稱AI)
同樣的,我們更新后也有更新后數據涉及的物理塊,如果更新的數據突然沒了,我們可以根據這個物理塊把更新的數據恢復到更新后的狀態。這就是重做(英文簡稱redo)。
上面可能聽的有點蒙,這么說吧,我們有兩個備份——前像和后像。其中前像就是一面通往過去的魔鏡,后像就是一個通往未來的過程,終點是更新結束。如果你更新到一半卡住了,說你更新了吧,你沒更新完,說你沒更新,你又開始更新了,那你此時就可以根據過去的魔鏡回到過去。如果你更新完成了,但是更新了啥你卻丟了。那這個時候你就可以利用后像重新走一次更新過程。
就拿修改表數據的相關操作來說,日志記錄以下情況:
for update op:BI AIinsert op:—— AIdelete op:BI ——事務狀態:記錄每個事務的狀態,方便在恢復的時候做不同的處理。
每個事務在提交的時候面臨兩種結局:
第一種是提交了才結束,這標志著事務成功地執行了,只有在事務提交后,事務對數據的影響才能被其他事務訪問。
第二種是由于事務本身或者外部的原因,事務失敗,也就是說這時候可能事務只做了一半,要繼續做的時候事務出問題終止了,這時候為了消除那做了一半的影響,我們會對其做出卷回。
如果是數據庫失效了,可以取出后備副本,然后根據運行的記錄,如果沒提交就將前像卷回,這叫向后恢復。如果事務已經被提交了,但是提交前和提交后中間添加的數據沒了,那我們必要時用后像重做,這叫向前恢復。
基于多副本的恢復技術
如果系統中有多個數據庫副本,而且這些副本具有獨立的失效模式,則可利用這些副本相互備份,用于恢復。所謂的獨立失效模式是指各個副本不會因為故障而一起失效。為了盡可能的創建這種環境,各副本的支撐環境要盡可能地獨立,比如不要共用電源、磁盤、控制器和CPU等等,反正就是盡可能不要共用東西就對了。這種技術在分布式數據庫用的比較多。
由于現在硬件價格下降,很多人都能買得起磁盤,所以在一些可靠性要求高的系統中,常常采用鏡像磁盤技術,鏡像磁盤技術為了使失效模式獨立,兩個磁盤系統有各自的控制器和cpu,但彼此可以相互切換。他的原理如下
-
DBMS自動把整個數據庫或其中的關鍵數據復制到另一個磁盤上
-
DBMS自動保證鏡像數據與主數據庫的一致性,每當主數據庫更新時,DBMS自動把更新后的數據復制過去,如圖:
出現介質故障時
沒有出現故障時,鏡像磁盤可用于并發操作,一個用戶對數據加排他鎖修改數據,其他用戶可以讀鏡像數據庫上的數據,而不必等待該用戶釋放鎖。
9.2 事務和日志
我們在這一講談到了多次事務,那事務(transaction)到底是啥呀。
9.2.1 事務
一個事務是一個完整的業務邏輯單元,不可再分。
比如:銀行賬戶轉賬,從A賬戶向B賬戶轉賬10000,需要執行兩條Update語句。
update t_act set balance = balance -10000 where actno = 'act_001';update t_act set balance = balance + 10000 where actno = 'act_002';以上這個過程是一起的,如果不一起,就會導致轉賬這件事失敗。也就是說,以上兩條DML語句必須同時成功,或者同時失敗,不允許出現一條成功,一條失敗。
要想保證以上的兩條DML語句同時成功或者同時失敗,那么就需要使用數據庫的“事務機制”。
需要注意的是,和事務相關的SQL語句只有DML語句,因為它們包含的添加、刪除、更新語句都是和數據庫表當中的“數據”相關的。事務的存在是為了保證數據的完整性,安全性。
有人會發出疑問:假設所有的事務都能使用一條DML語句搞定,還需要事務機制嗎?如果真的出現上述情況,那么確實不需要事務。但實際情況不是這樣的,通常一個事務需要多條DML語句共同聯合完成。
事務可以保證多個操作原子性,要么全成功,要么全失敗。對于數據庫來說事務保證批量的DML要么全成功,要么全失敗。綜上所述,事務具有四個特征:ACID。
-
A原子性:整個事務中的所有操作,必須作為一個單元全部完成(或者全部取消),即:要么不做,要么全做
-
C一致性:在事務開始之前和結束之后,數據庫都保持一致狀態
-
I隔離性:一個事務不會影響其他事務的運行
-
D持久性:持久性說的是最終數據必須持久化到硬盤文件中,事務才算成功的結束。也就是說,一個成功執行的事務對數據庫的影響是持久的,即使數據庫因故障而受到破壞,DBMS也應該能夠恢復
我們來看看一個事務的案例:
Begin transactionread AA:=A-sif A<0 then Display "insufficient fund"Rollback /*undo and terminate*/else B:=B+sDisplay "transfer complete"Commit /*commit the update and terminate*/Rollback --abnormal termination Commit --normal termination假如現在做一個銀行轉賬系統,A給B轉了s元,如果A余額不足,那么就會提示:insufficient fund(余額不足),然后把A余額-s元的這個操作回滾。
如果余額足夠,那么A-s,B+s,然后提示轉賬成功,然后事務結束,利用Commit提交事務。
9.2.2 運行記錄的結構
9.2.2.1 活動事務表
記錄運行記錄的有多個結構,與操作系統類似,操作系統中有記錄進程的進程塊PCB,對于事務來說則有標識符(Transaction Identifier,TID)。對于所有正在執行,尚未提交的事務,其身上的標識符都會在活動事務表(Active Transacion list,ALT)中占有一條元組。活動事務表會給這些事務貼上正在執行、尚未提交的標識符。
9.2.2.2 提交事務表
提交事務表(Committed Transaction list,CTL)記錄所有已經提交的事務的標識符。
當你把一個事務要提交了,那活動事務表會把TID交給提交事務表,然后在活動事務表中把該TID刪除。但是,這兩者順序是不可顛倒的,如果顛倒,萬一停電,就會導致TID剛在活動事務表中刪除,然后在添加進提交事務表的那一刻由于故障沒有任何記錄。
9.2.2.3 日志
對于數據庫來說,日志(log)一般記錄了操作的流水記錄。其一般存儲在非揮發存儲器(非易失存儲器)中。
存儲器一般分為主存和輔存。
主存通常指隨機訪問存儲器(RAM),一般分為兩種:一種是易失性隨機訪問存儲器,如RAM(磁芯存儲器)、CMOS;另外一種是非易失性隨機訪問存儲器,它們和RAM不同,遇到突然的外界因素不會丟失其內容。例如:ROM(只讀存儲器)、EEPROM(電可擦除可編程ROM)、閃存。
日志主要包含TID,前像文件和后像文件。具體結構如下:
其中前像文件中的BID記錄的是物理塊的地址,空白部分記錄的是正在更新事務的老值,這一個矩形就相當于一個物理塊,前像文件相當于一個堆文件,有很多物理塊堆疊而成,如果一個關系需要卷回,就可以從前像文件中找出該事務的所有前像塊,然后把它們全部寫入由于故障被破壞的關系的對應塊中從而消除該事務對數據庫所產生的影響。同理,后像文件也和前像文件的結構一樣。
在每個日志中都應該記錄前像文件和后像文件,以方便供以恢復使用。
9.2.3 提交規則和先記后寫規則
9.2.3.1 提交規則
后像(新值)必須在事務提交前寫入非揮發存儲器中,不能放在內存中,因為內存會消失。
也就是說,后像可以放數據庫也可以放日志中(前面說過日志也是一個非揮發存儲器);萬一這時候故障,即使沒有放數據庫,我們也可以利用日志中的后像文件中的后像重做;如果這時候有其他事務要訪問這些數據,由于更新的內容已經保存在后像文件里面了,也可能在緩沖區中(因為后像文件是由多個物理塊組成的),所以其他事務仍然可以去緩沖區或者后像文件找哪些更新后的內容。
9.2.3.2 先記后寫規則
如果后像在事務提交前寫入了數據庫,那必須把前像首先記入運行記錄。
這句話可能有點抽象,我們舉個例子。假如我現在在做筆記,如果我沒有保存以前寫的舊筆記,直接把舊筆記撕了,然后直接寫新筆記,這時候一旦突然斷電,那就有可能新筆記和老筆記都沒了。先記后寫規則就是為了防備這一點。
9.3 更新策略以及故障后的恢復
更新操作無非就是undo或redo。需要注意的是,undo操作和redo操作具有冪等性。
舉個例子:假如在編程中,我們設x = 10,后面又寫了一條x = 20,那么就能把x = 10覆蓋掉,其中后像20,前像10,但是后像20覆蓋前像10的時候,不管覆蓋多少次都是20,這就是冪等性,即做多次操作和一次操作的效果是等價的。
更新操作并不是上來就無腦搞,是有一定策略的。目前理論上的更新策略有以下幾種。
后像在事務提交前已經完全寫入數據庫(沒人采用)
策略具體步驟如下:
TID→ATL
BI→log
AI→DB,log
TID→CTL
從ATL刪除TID
在每個數據庫系統里總會有這么個模塊——重啟動模塊,不同數據庫系統叫法可能不同;他的作用是在重啟動數據庫系統后,檢查上一次退出是否正常退出。
在用重啟動模塊檢查數據庫系統時,會出現下列三種情況:
| 有 | - | 1已經完成,但4沒有完成 | ①若BI已經寫入log則undo,否則無需undo ②從ATL刪除TID |
| 有 | 有 | 4執行完成 | 從ATL刪除TID |
| - | 有 | 5執行完成 | 無需處理 |
- 如果ATL里有而CTL沒有,說明在提交階段出問題了,那么系統會自動去undo收回那些事務。
- 如果ATL有,CTL也有,說明事務已經提交,但是ATL刪除TID的時候出問題了,那么系統就會去刪除TID。
- 如果ATL沒有,CTL有,說明過程執行沒有出現差錯,事務提交完畢。
后像在事務提交后才寫入數據庫(目前主流)
策略具體步驟如下:
TID→ATL
AI→log
TID→CTL
AI→DB
從ATL刪除TID
TID提交ATL說明執行為提交,這時候由于后像沒有在事務提交前寫入數據庫,所以不必記錄前像,這時把后像記到日志,然后TID提交給CTL,這時候確定后像提交了,然后再把AI給DB,然后在ATL刪除TID。
在用重啟動模塊檢查數據庫系統時,會出現下列三種情況:
| 有 | - | 1已經完成,但4沒有完成 | ①從ATL刪除TID |
| 有 | 有 | 3已經完成,但5沒有完成 | ①redo ②從ATL刪除TID |
| - | 有 | 5執行完成 | 無需處理 |
-
如果檢查時發現ATL有TID而CTL沒有,說明提交CTL過程出錯,這時候由于還未在其他地方做出任何操作,無須undo,只需在ATL刪除TID即可。
-
如果檢查ATL和CTL都有TID,那么說明ATL刪除TID出錯,甚至AI搬到DB的階段搬到一般故障了也有可能,所以這時候需要重新開始搬,即redo,然后在ATL里刪除TID。由于冪等性,即使你前面搬到一半,現在redo重新搬也不會搬多。
-
如果ATL沒有而CTL有說明過程結束了,無須任何操作。
后像在事務提交前后寫入數據庫(現在過時了)
策略思想:在第二個策略的基礎上,人們想了一個辦法,同樣的不將后像在事務提交前急忙寫入數據庫,而是創建一個后臺進程,當數據庫空閑的時候后臺進程將其喚醒,開始把后像搬到數據庫,一旦數據庫忙起來了,就暫停搬運。
TID→ATL
AI,BI→log
AI→DB(部分寫入)
TID→CTL
AI→DB(繼續完成)
從ATL刪除TID
在用重啟動模塊檢查數據庫系統時,會出現下列三種情況:
| 有 | - | 1已經完成,但4沒有完成 | ①若BI已經寫入log則undo,否則無需undo ②從ATL刪除TID |
| 有 | 有 | 4已經完成,但6沒有完成 | ①redo ②從ATL刪除TID |
| - | 有 | 6執行完成 | 無需處理 |
-
如果ATL有TID而CTL沒有,說明BI可能有一部分在搬到數據庫了,這時候需要做undo。
-
如果ATL有,CTL也有,說明AI可能搬到一半,ATL還沒刪除TID,所以為了繼續搬完,我們要根據前像做redo,搬完后在ATL刪除TID。
-
如果ATL和CTL都有,說明過程全部執行完畢,無須操作。
總結
以上是生活随笔為你收集整理的数据库杂谈(九)——事务管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图片存储解决方案--阿里云OSS
- 下一篇: java gdal postgresql