SQL 事務和 ACID 屬性
引言:SQL 中的事務
想象一下一個使用 SQL 作爲數據庫的銀行系統。用戶 A 想將一些錢存入用戶 B 的賬戶。如果他們發送了錢,我們從他們的賬戶餘額中扣除了這筆錢,並且我們想要將這筆錢存入用戶 B 的賬戶,但突然間我們的數據庫崩潰了。
這是否意味着我們從用戶 A 的餘額中扣除的錢消失了?嗯,並不是在 SQL 數據庫中,因爲它們使用 SQL 事務來處理這些情況。
事務和 ACID 屬性
事務是一系列作爲單一原子單元執行的一個或多個 SQL 操作。其目的是確保數據庫中的數據一致性。事務具有以下屬性,通常被稱爲 ACID 屬性的首字母縮寫:
• 原子性(Atomicity): 整個事務被視爲單個單元,要麼完全成功,要麼完全失敗。• 一致性(Consistency): 事務將數據庫從一個有效狀態轉換爲另一個有效狀態,保持數據庫的不變性。• 隔離性(Isolation): 併發事務的修改在它們提交之前被隔離開來。• 持久性(Durability): 一旦事務已經提交,即使系統發生故障,它也將保持提交狀態。
SQL 事務中的關鍵命令
我們使用 BEGIN TRANSACTION 關鍵字標記 SQL 事務的開始。
爲了保存事務中進行的所有更改,我們將更改 COMMIT 到數據庫。
如果在事務中發生了錯誤,我們可以使用 ROLLBACK 命令回滾事務中進行的所有更改,將數據庫恢復到事務開始時的狀態。
示例
讓我們回到我們簡單的銀行應用程序示例,其中您需要從賬戶 A 轉賬 100 美元到賬戶 B。這涉及兩個步驟:
- 從賬戶 A 的餘額中扣除金額 2. 將其添加到賬戶 B 中
爲了使事務成功,必須完成這兩個步驟。下面是如何將其寫爲 SQL 事務的示例:
BEGIN TRANSACTION;
-- 扣除賬戶A的餘額100美元
UPDATE Accounts
SET balance = balance - 100
WHERE account_id = 'A';
-- 檢查賬戶A是否有足夠的餘額,如果沒有就回滾
IF @@ROWCOUNT = 0
ROLLBACK;
-- 將100美元添加到賬戶B的餘額
UPDATE Accounts
SET balance = balance + 100
WHERE account_id = 'B';
-- 如果一切正常,則提交事務
COMMIT;
此事務執行以下操作:
- 啓動事務 以確保以下操作是單個原子過程的一部分。2. 從賬戶 A 中扣除 100 美元: 假設有一個名爲
**accounts**
的表,其中包含列**account_id**
和**balance**
。3. 檢查賬戶 A 是否有足夠的資金: 如果賬戶 A 沒有足夠的錢,使用**ROLLBACK TRANSACTION**
回滾事務,取消所有更改。4. 將 100 美元添加到賬戶 B 中: 如果賬戶 A 有足夠的錢,就向賬戶 B 添加 100 美元。5. 提交事務: 如果兩次更新都成功,則執行**COMMIT TRANSACTION**
命令,將在此事務期間進行的更改永久應用到數據庫。
這確保了要麼兩個賬戶都適當地更新,要麼在任何一點出現問題時都不應用任何更改,從而保持數據的完整性。
SQL 事務的隔離層級
數據庫事務的隔離級別確定事務的完整性如何維護以及在多大程度上每個事務與其他事務隔離。
SQL 標準定義了四個隔離級別,它們在一致性和性能之間進行權衡。
1. 讀未提交(Read Uncommitted)
• 描述: 隔離級別最低。事務甚至可以看到其他事務尚未提交的更改。• 對示例的影響: 在轉賬過程中,如果另一個事務正在更新賬戶 A 或 B 的餘額,這個事務可能讀取這些尚未提交的值。這可能導致看到一個實際上並不存在的餘額(如果其他事務失敗並回滾)。
2. 讀提交(Read Committed)
• 描述: 確保事務只能讀取已提交的數據。• 對示例的影響: 通過確保只讀取已提交的賬戶 A 和 B 餘額,避免了 “讀未提交” 的問題。然而,在事務內多次讀取餘額時,
如果其他事務正在修改數據,它可能看到不同的值(不可重複讀)。
3. 可重複讀(Repeatable Read)
• 描述: 確保如果事務第二次讀取數據,它將找到相同的數據值(避免不可重複讀)。• 對示例的影響: 這個級別防止事務在事務內多次讀取相同數據時看到其他事務所做的更改。在餘額檢查和更新操作期間,它有助於保持一致的讀取結果。然而,它可能不會阻止幻讀(其他事務添加的新行)。
4. 可序列化(Serializable)
• 描述: 最高級別的隔離。事務完全與其他事務隔離,就像它們是串行執行的一樣。• 對示例的影響: 這確保完全隔離。沒有其他事務可以干擾轉賬過程。它阻止所有併發問題(髒讀、不可重複讀和幻讀),但以減少併發性和可能引起鎖定的性能問題爲代價。
使用不同的隔離級別,可能會發生幾種現象,比如髒讀、不可重複讀或幻讀。讓我們看看這些術語的含義:
髒讀
髒讀發生在一個事務讀取由併發未提交事務寫入的數據時。因此,如果其他事務回滾,第一個事務將讀取從未正式提交到數據庫中的數據。
示例:
• 事務 1 開始並從賬戶 A 轉賬 100 美元到賬戶 B。• 在事務 1 提交之前,事務 2 開始並讀取賬戶 A 的餘額。• 如果事務 1 失敗並回滾,事務 2 已讀取一個從未正式提交的餘額。
不可重複讀(讀未提交)
當事務在其過程中兩次檢索同一行時,並且兩次讀取之間行內的值發生更改時,就會發生不可重複讀。實質上,另一個事務在兩次讀取之間修改了該行。
示例:
• 事務 1 開始並讀取賬戶 A 的餘額。• 事務 2 從賬戶 A 轉賬 100 美元到賬戶 B 並提交。• 事務 1 再次讀取賬戶 A 的餘額,並看到與之前不同的餘額。
幻讀
幻讀發生在一個事務期間,另一個事務通過添加(或刪除)行到正在讀取的記錄中來更改數據。這意味着事務中的後續讀取可能返回包含新添加行的一組行,或者不包括原始讀取的已刪除行。
示例:
• 事務 1 開始查詢賬戶 A 的交易數量。• 事務 2 爲賬戶 A 插入了一條新的交易記錄並提交。• 事務 1 再次查詢賬戶 A 的交易數量,發現比之前多出了一些交易。
顯然,更高的隔離級別減少了可能發生的現象類型,但以減少併發性和潛在的性能影響爲代價。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/pQ4qHTTicO9-U-n4EfNwrA