事務處理基本概念

Chasssser發表於2024-03-25

目錄
  • 事務處理
    • 1.1 基本概念
    • 1.2 一個簡單的事務模型例子
    • 1.3 儲存結構
    • 1.4 事務的原子性和永續性
    • 1.5 事務的排程 & 可序列化
    • 1.6 事務的隔離性和原子性
    • 1.7 事務隔離性級別
    • 1.8 隔離性級別的實現
      • 1.8.1 鎖
      • 1.8.2 時間戳
      • 1.8.3 多版本和快照隔離

事務處理

1.1 基本概念

事務,transaction,是構成單一邏輯工作單元的操作合集。

事務通常由高階資料操縱語言,比如SQL或者程式語言(比如C++)透過JDBC或者ODBC嵌入式資料庫訪問程式進行執行。

事務基本內容由類似語句beign transactionend transaction語句來規定,兩者之間的操作集合作為必須作為一個單一、不可分割的單元出現,屬於這個合集中的操作要麼都執行,要麼都不執行,這種特性稱為原子性(atomicity)

並且資料庫必須採取特殊處理來保證事務的正常執行而不被併發執行的資料庫語句干擾,這稱為隔離性(isolation)。、

當資料庫系統崩潰後,事務的操作也必須是持久的,這稱為永續性(durability)

除此之外,事務必須保持資料庫的一致性(consistency),即如果一個事務作為原子從一個一致的資料庫狀態開始獨立執行,則事務結束時資料庫也必須是再次一致的。

以上性質稱為ACID特性,縮寫來自4條性質的第一個字母。

1.2 一個簡單的事務模型例子

現假設事務使用如下兩個操作來訪問資料:

  • read(x),從資料庫把資料項x讀取到執行該操作事務的主存緩衝區內的一個變數x中。
  • write(x),從執行該操作事務的主存緩衝區的變數x中把資料項x寫入資料庫中。

事務例子\(T_i\)如下:

\[\begin{align} & read(A);\\ & A:= A-50;\\ & write(A);\\ & read(B);\\ & B:=B+50;\\ & write(B).\\ \end{align} \]

從上面的例子上看,如果考慮ACID特性:

  • 一致性,一致性要求該事務的執行不改變AB之和;
  • 原子性,假設事務執行之前A和B的值分別為1000和2000,那麼如果故障發生在write(A)之後write(B)之前,此時系統中A和B的值就是950和2000,造成了這種不一致狀態;
    • 不一致狀態並非一定是故障產生的,即使正常執行的情況下,系統也會在某一個時刻處於不一致狀態,但是原子性則要求這種不一致狀態只能在執行當中存在,其他時刻不存在;
    • 原子性要求,事務中的所有動作要麼全部執行,要麼都不執行。
    • 保證原子性思路:對於事務要執行寫操作的資料項,資料庫系統會在磁碟上記錄其舊值,該資訊記錄在日誌中;如果事務執行失敗,資料庫系統就會從日誌中恢復其舊值,將系統還原成事務執行之前的狀態。保證原子性是資料庫本身的責任,由恢復系統(recovery systeem)處理。
  • 永續性,當事務執行成功後,系統就需要保證該事務對資料庫所做的所有更新都是成就的,即使在事務執行之後系統出現故障。
    • 保證永續性:同樣由恢復系統處理,且基於一個前提,記憶體中的資料會在故障後丟失,但是磁碟中的資料庫不會丟失。所以只需要確保以下兩個條件的任一條件即可:
      • 事務執行後的更新在事務結束之前已經寫入磁碟。
      • 事務的已經執行的更新資訊也已經寫入磁碟上,且該資訊可以幫助系統故障後恢復。
  • 隔離性,當幾個事務併發執行,即使每個事務都保證了原子性和一致性,但是事務的交叉執行仍然會導致不一致的狀態。
    • 事務序列執行可以避免產生問題,但是效率太低;
    • 資料庫系統中的併發控制系統(concurrency-control system)可以確保隔離性,具體來說,確保事務併發執行後的系統狀態與這些事務以某種次序序列執行的狀態等價。

1.3 儲存結構

  • 易失性儲存,volatile storage,比如記憶體,其中的資料在系統崩潰後丟失;
  • 非易失性儲存,nonvolatile storage,比如磁碟, 資料在系統崩潰後不會丟失;
  • 穩定性儲存,stable storage,資料“永遠”不會丟失,透過某些手段降低了故障可能性。

1.4 事務的原子性和永續性

當事務執行失敗時,就需要中止事務,且為了保證原子性,中止事務必須對資料庫的狀態不造成影響,即對資料庫所做的所有改變都必須撤銷。

成功完成執行的事務稱為已提交,而一旦事務已提交,就不能透過中止事務來撤銷其造成的影響。

撤銷已提交事務造成的影響的唯一方法是執行一個補償事務(compensating transaction),這個責任被交給了使用者而不是資料庫系統來處理。

事務的狀態轉換圖如上所示,事務從活動狀態開始,當事務執行完它的最後一條語句之後就進入部分提交狀態

此時,事務已經完成了語句的執行,但是實際輸出可能還沒有寫入磁碟,如果此刻發生故障,事務就可能中止。

如果順利,那麼資料庫系統就會往磁碟上寫入相關資訊,以確保系統可以在故障重啟後重新建立事務的更新,之後事務就進入提交狀態

當一個事務進入部分提交狀態後,只有在沒有其他併發事務已經修改該事務想要更新的資料項的情況下,事務才會進入提交狀態,而不能提交的事務則中止。

當系統判斷事不能正常執行後,事務就會進入失敗狀態,此時事務就必須回滾。接著事務就會進入中止狀態,此時系統有兩種選擇:

  • 重啟事務,僅當硬體故障導致的事務中止時使用,其重啟的事務會被看作是一個新的事務;
  • 殺死事務,當中止原因是事務內部邏輯錯誤時;

同時需要小心處理可見的外部寫(observable external write)動作,比如寫入使用者螢幕時,一般的處理方式是在磁碟上臨時寫入相關資料,在事務進入提交狀態之後再進入外部寫操作。

1.5 事務的排程 & 可序列化

在併發執行中,保證併發執行的效果和沒有併發時相同,就可以確保資料庫的一致性,即排程在某種程度上等價於一個序列排程,這種排程稱為可序列化。

併發控制的前提是,如何保證一個排程是可序列化的?關鍵在於指令之間的衝突問題。

對於AB兩個不同事務在同一個資料項上的操作,當其中有一個寫指令時,兩個指令就是衝突的,因為執行次序和結果強相關。

這裡有幾個概念:

  • 衝突等價,conflict equivalent,當排程S可以經過一系列非衝突指令交換得到S’,那麼兩個排程是衝突等價的;
  • 衝突可序列化,conflict serializable,如果排程S和一個序列排程衝突等價,那麼排程S就是衝突可序列化的。

而序列化順序可以透過拓撲排序得到,要判定衝突可序列化,就需要構造優先圖,並檢測是否存在環,如果存在則說明該排程不是衝突可序列化的。

1.6 事務的隔離性和原子性

在併發執行過程中,事務失敗的影響必須被撤銷才能保證其原子性。而從事務故障恢復的角度來看以下兩種排程才是可以接受的排程:可恢復排程和無級聯排程。

可恢復排程,對於每對事務\(T_i\)\(T_j\),如果\(T_j\)讀取了\(T_i\)之前寫的資料項,那麼\(T_i\)必須先於\(T_j\)提交。依賴事務在被依賴事務之前提交。

一個不可恢復排程的例子如下:

事務\(T_7\)\(read(A)\)之後立刻提交了,但是此時\(T_6\)還在活躍狀態,\(T_7\)讀取了\(T_6\)寫入A的值。如果\(T_6\)提交之前發生故障,因為\(T_7\)依賴於\(T_6\)(讀取了\(T_6\)之前寫入的資料),所以\(T_7\)也必須回滾。

但是因為\(T_7\)已經提交,不能中止\(T_7\)來保證事務的原子性,所以在\(T_6\)發生故障後不能正確恢復。

無級聯排程,對於每對事務\(T_i\)\(T_j\),如果\(T_j\)讀取了先前由\(T_i\)所寫的資料項,那麼\(T_i\)必須在\(T_j\)​讀操作之前提交,每個無級聯排程也是客回覆排程,區別在於,無級聯排程要求被依賴事務必須在依賴事務的讀操作之前提交

一個級聯回滾(cascading rollback)的例子如下,當事務\(T_8\)失敗需要回滾時,因為\(T_9\)\(T_{10}\)都依賴於\(T_8\),一個事務故障導致一系列事務回滾,需要撤銷大量工作。

1.7 事務隔離性級別

SQL規定的隔離性級別如下:

  • 可序列化
  • 可重複讀,只允許讀取已提交的資料,且在一個事務內兩次讀取同一個資料項之間,其他事務不能更新該資料。但該事務不要求於其他事務可序列化。
  • 已提交讀,只允許讀取已提交資料,但不要求可重複讀。
  • 未提交讀,允許讀取未提交資料,這是SQL允許的最低一致性級別。

以上所有隔離級別都不允許髒寫(dirty write),即如果一個資料項已經被另外一個尚未提交或者中止的事務(正常活躍的事務)寫入,就不允許對該資料項執行寫操作

大多數資料庫預設級別是已提交讀,也可以顯示設定隔離性級別,而修改級別必須作為事務的第一條語句執行

1.8 隔離性級別的實現

在資料庫系統中,透過多種併發控制機制(concurrency-control scheme)來保證多個事務鬢髮執行時,只產生可接受的排程。比如鎖、時間戳和快照隔離。

1.8.1 鎖

一個事務可以封鎖其訪問的資料項,而不用封鎖整個資料庫。透過持有鎖來保證序列化,但是持有周期不能過長否則會影響效能。

鎖一般有兩種,共享鎖用於讀操作,排他鎖用於寫操作。多個事務可以同時持有同一個資料項上的共享鎖,但只有當其他事務在一個資料項上不持有任何鎖時,一個事務才允許持有該資料項的排他鎖。

以上兩種鎖模式以及兩階段協議在保證可序列化的前提下允許資料的併發讀。

1.8.2 時間戳

對於每個資料項,系統維護兩個時間戳,讀時間戳記錄讀取該資料項的事務的最近時間戳;寫時間戳記錄寫入該資料項的當前值的事務的時間戳;

當訪問衝突的時候,事務會按照時間戳的順序來訪問資料項,當無法訪問時,違例事務會中止,然後分配一個新的時間戳開始。

1.8.3 多版本和快照隔離

透過維護資料項的多個版本,一個事務就可以讀取一箇舊版本的資料項,而不是被另一個未提交或者在序列化序列中排在後面的事務寫入的新版本資料項。

多版本維護,廣泛使用的是快照隔離(snapshot isolation)技術。

快照隔離中,每個事務開始時有自身資料庫版本或者快照,它從這個版本中讀取資料,來和其他事務的更新隔離開。

即當事務更新資料庫時,更新只會出現在其私有版本而不是資料庫本身,直到事務提交時,更新才會被寫入資料庫。

快照隔離可以保證讀資料的嘗試無需等待,因為讀取的是它自己的快照,且大部分事務都是隻讀的,所以和鎖相比會帶來效能改善。

但是快照隔離下,任何事務都不能看到對方的更新,這可能會導致不一致狀態出現。

相關文章