文章目錄
- 一、事務的ACID特性
- 二、數據庫原理例題與 ACID 特性判斷
- 三、拓展(undolog 與 redolog)
一、事務的ACID特性
綜述:
- 原子性(Atomicity):事務是不可分割的最小操作單元,要么全部成功,要么全部失敗。
- 一致性(Consistency):事務完成時,必須使所有的數據都保持一致狀態。
- 隔離性(Isolation):數據庫系統提供的隔離機制,保證事務在不受外部并發操作影響的獨立環境下運行。
- 持久性(Durability):事務一旦提交或回滾,它對數據庫中的數據的改變就是永久的。
-
原子性(Atomicity):
- 通俗類比: 就像你去ATM取錢。整個取錢操作(輸入密碼、選擇金額、出鈔、打印憑條)是一個原子操作。如果中間任何一步失敗了(比如機器故障沒出鈔),那么整個操作就會被撤銷,你的賬戶余額不會改變,就像你沒取過錢一樣。如果全部成功,你的賬戶余額才會減少,你拿到錢。
- 核心思想: 事務中的所有操作要么作為一個整體被執行成功,要么作為一個整體被回滾(撤銷)。沒有中間狀態。
- 實現方式: 通常通過日志(如Undo Log)來實現。如果事務執行過程中發生錯誤,系統會根據日志回滾到事務開始前的狀態。
-
一致性(Consistency):
- 通俗類比: 想象一個銀行賬戶的總金額。在任何一個事務執行前后,所有賬戶的總金額應該保持不變(假設沒有外部資金流入流出)。如果你從賬戶A轉賬100元到賬戶B,事務開始前,A+B的總金額是X。事務結束后,A減少100,B增加100,A+B的總金額仍然是X。這就是一致性。
- 核心思想: 事務必須使數據庫從一個一致性狀態轉換到另一個一致性狀態。一致性狀態是指數據庫滿足所有的預定義規則和約束(例如,主鍵唯一、外鍵引用有效、余額不能為負等)。
- 實現方式: 依賴于原子性、隔離性、持久性,以及數據庫本身的約束(如外鍵約束、唯一性約束、Check約束等)。程序員編寫的業務邏輯也需要保證一致性。
-
隔離性(Isolation):
- 通俗類比: 想象你在圖書館看書,旁邊有人也在看書。隔離性就像是你們之間有隔板,互不影響。你在翻頁、做筆記,旁邊的人也在做自己的事情,你們不會看到對方操作的中間狀態,也不會互相干擾。
- 核心思想: 并發執行的事務之間互不干擾,每個事務都感覺自己是系統中唯一正在運行的事務。一個事務的中間結果對其他事務是不可見的。
- 實現方式: 數據庫提供了多種隔離級別(如讀未提交、讀已提交、可重復讀、串行化),通過鎖機制、多版本并發控制(MVCC)等技術來實現不同程度的隔離。
-
持久性(Durability):
- 通俗類比: 你在電腦上寫了一篇文檔,點擊了“保存”。即使電腦突然斷電,當你重新開機時,你保存的文檔依然存在。這就是持久性。
- 核心思想: 一旦事務提交成功,它對數據庫數據的改變就是永久性的,即使系統發生故障(如斷電、崩潰)也不會丟失。
- 實現方式: 通常通過將事務的修改寫入到日志文件(如Redo Log)中來實現。即使數據還沒有完全寫入磁盤,只要相關的日志已經寫入磁盤,系統恢復時就可以通過日志來重做(Redo)這些操作,恢復到事務提交后的狀態。
二、數據庫原理例題與 ACID 特性判斷
下面我們通過一些例子來幫助你判斷和理解這些特性。請判斷每個例子主要體現了事務的哪種 ACID 特性。
例題 1:
你在一個電商網站上下單購買商品。這個操作包括:
- 從庫存表中減少商品數量。
- 在訂單表中插入一條新的訂單記錄。
- 從用戶余額中扣除支付金額。
如果在執行過程中,扣除用戶余額時發生了網絡錯誤導致失敗,整個下單操作被取消,庫存數量和訂單記錄都沒有發生改變。
請問:這個例子主要體現了事務的哪種特性?為什么?
答案與解析:
這個例子主要體現了事務的 原子性(Atomicity)。
原因:整個下單過程被視為一個不可分割的單元。當其中一個步驟(扣除用戶余額)失敗時,整個事務被回滾,之前已經執行的步驟(減少庫存、插入訂單)也被撤銷,系統回到了事務開始前的狀態。這符合原子性“要么全部成功,要么全部失敗”的定義。
例題 2:
一個銀行系統中有規定:所有賬戶的總金額必須大于等于零。
現在有兩個賬戶 A 和 B,A 有 1000元,B 有 500元。
一個事務從 A 賬戶轉賬 200元到 B 賬戶。
事務執行完成后,A 賬戶變為 800元,B 賬戶變為 700元。
此時,所有賬戶的總金額(800 + 700 = 1500)仍然大于等于零,符合銀行系統的規定。
請問:這個例子主要體現了事務的哪種特性?為什么?
這個例子主要體現了事務的 一致性(Consistency)。
原因:事務執行前后,數據庫從一個一致性狀態(所有賬戶總金額 >= 0)轉換到了另一個一致性狀態(所有賬戶總金額 >= 0)。盡管賬戶A和B的金額發生了變化,但它們的變化是相互抵消的,維護了數據庫的業務規則和約束。
例題 3:
兩個用戶同時嘗試購買同一件限量商品。
用戶 A 的事務開始執行,查詢到商品庫存為 1。
用戶 B 的事務幾乎同時開始執行,也查詢到商品庫存為 1。
用戶 A 的事務執行扣減庫存操作,庫存變為 0,然后提交。
用戶 B 的事務也執行扣減庫存操作,如果隔離性不好,它可能會在用戶 A 提交前就看到庫存為 1,然后也嘗試扣減,導致庫存變為 -1(超賣)。
但由于數據庫的隔離機制,用戶 B 在嘗試扣減庫存時,會發現庫存已經被用戶 A 修改為 0,從而導致用戶 B 的扣減庫存操作失敗(或者被阻塞等待)。最終只有一個用戶購買成功,庫存變為 0。
請問:這個例子主要體現了事務的哪種特性?為什么?
這個例子主要體現了事務的 隔離性(Isolation)。
原因:在并發環境下,多個事務同時訪問和修改相同的數據。隔離性確保了每個事務的執行不受其他并發事務的干擾。在這個例子中,良好的隔離性防止了“超賣”問題的發生,保證了用戶 B 在用戶 A 提交后能夠看到最新的庫存狀態,避免了基于過期數據進行操作。
例題 4:
你成功地完成了一筆訂單支付,事務提交成功。
即使緊接著數據庫服務器發生了突然的斷電,當你重新啟動數據庫后,查詢訂單列表,仍然能夠找到剛才支付成功的訂單記錄,并且你的賬戶余額也確實被扣除了。
請問:這個例子主要體現了事務的哪種特性?為什么?
這個例子主要體現了事務的 持久性(Durability)。
原因:一旦事務提交成功,其對數據庫的修改就被認為是永久的,即使系統發生故障,這些修改也不會丟失。數據庫通過將提交的事務信息寫入日志等方式來保證數據不會因為意外情況而丟失。
三、拓展(undolog 與 redolog)
Q1: 詳細解釋一下,數據庫是如何實現 原子性 的嗎?特別是當事務執行到一半發生崩潰時,數據庫如何確保之前已經執行的操作被撤銷?
A: 原子性是通過日志來實現的。數據庫會記錄事務的每一步操作。如果事務失敗了,就可以根據日志把已經執行的操作撤銷掉,回到事務開始前的狀態。
Q2: “日志”具體是指哪種日志?撤銷操作的原理是什么?
A: 數據庫主要依賴 Undo Log 來實現原子性。Undo Log 記錄了數據修改前的值。如果事務需要回滾,系統會讀取 Undo Log,根據日志中記錄的舊值將數據恢復到修改前的狀態。
Q3: 那么,Undo Log
是在事務提交后才清除嗎?如果在事務提交前數據庫崩潰了,Undo Log
起什么作用?
A: Undo Log 不是在事務提交后立即清除,而是會保留一段時間,直到相關的事務徹底完成并且不再需要回滾。如果在事務提交前數據庫崩潰了,當數據庫重啟進行恢復時,會檢查未完成的事務,并利用 Undo Log 將這些未完成事務已經進行的修改回滾,確保這些事務是“不存在”的,這正是原子性的體現。
Q4: 你提到了 Undo Log
用于回滾。那么,持久性 是如何保證的呢?它依賴哪種日志?如果事務提交后,數據還沒完全寫入磁盤,但日志已經寫入了,然后數據庫崩潰了,數據還能恢復嗎?
A: 持久性主要依賴 Redo Log。Redo Log 記錄了數據修改后的值。當事務提交時,數據庫會確保 Redo Log 相關的記錄已經寫入磁盤。即使數據頁本身還沒來得及寫入磁盤,只要 Redo Log 已經持久化了,系統恢復時就可以通過讀取 Redo Log 來重做(Redo)這些操作,將數據恢復到事務提交后的狀態。
Q5: 所以,Redo Log
和 Undo Log
在保證ACID特性中扮演了不同的角色。你能總結一下它們各自的主要作用嗎?
A: 好的。簡單來說,Undo Log 主要用于保證 原子性,它記錄了數據修改前的狀態,用于回滾事務。Redo Log 主要用于保證 持久性,它記錄了數據修改后的狀態,用于在系統崩潰恢復時重做已提交的事務。它們是數據庫實現事務ACID特性的重要底層機制。
Q6: 你提到了 隔離性 有不同的級別,比如讀未提交、讀已提交、可重復讀、串行化。你能簡單解釋一下“讀未提交”隔離級別可能帶來的問題嗎?
A: 讀未提交(Read Uncommitted)是隔離級別最低的。它允許一個事務讀取另一個未提交事務的修改。這可能導致 臟讀(Dirty Read) 問題。例如,事務A修改了一行數據但還沒有提交,事務B讀取了這行被修改但未提交的數據。如果事務A隨后回滾了這次修改,那么事務B讀取到的數據就是無效的、不存在的數據,這就是臟讀。