關聯式資料庫與交易(Transaction)機制入門
更新日期: 2025 年 3 月 4 日
本文為 SQL 關聯資料庫 基本介紹系列文,第 4 篇:
- 關聯式資料庫與資料完整性:初學者指南
- SQL 觸發器(Triggers):自動執行的資料庫機制
- Django 信號(Signals) vs SQL 觸發器(Triggers):關係與差異解析
- 關聯式資料庫與交易(Transaction)機制入門 👈進度
- 深入理解死結(Deadlock)與發生條件
- DCL(資料控制語言)入門:SQL 權限管理基礎
- SQL 儲存程序(Stored Procedure)入門
- ORM(對象關係對映):讓資料庫操作更簡單的工具
- SQL 儲存程序 vs ORM:如何選擇最適合的數據庫操作方式?
- 關聯式資料庫 View(檢視)是什麼?完整指南
- 理解 Materialized View:初學者指南
在現代應用開發中,關聯式資料庫(Relational Database)是最常見的數據存儲方式之一。
無論是企業管理系統、電商平台,還是社交媒體,關聯式資料庫都扮演著關鍵角色。
而在管理資料時,確保資料的一致性與可靠性是至關重要的,這正是「交易(Transaction)」的核心概念。
本篇文章將帶領初學者,了解關聯式資料庫的基本概念,以及交易的運作原理。
幫助你建立扎實的基礎,為後續學習進階資料庫技術鋪路。
什麼是關聯式資料庫?
關聯式資料庫(Relational Database,簡稱 RDB)是一種基於關聯模型(Relational Model)的資料庫系統,其核心概念來自於數學中的「集合論」。
它以表格(Tables)的形式來儲存資料,透過行(Rows)與列(Columns)來組織數據,並透過關聯(Relationships)將不同表格的數據互相關聯。
關聯式資料庫的基本元素
關聯式資料庫的基本組成元素包括:
- 表(Table):存放數據的主要結構,由行和列組成。
- 行(Row):也稱為「記錄(Record)」,表示一筆具體的數據。
- 列(Column):也稱為「欄位(Field)」,表示某一類型的數據。
- 主鍵(Primary Key, PK):唯一標識每一筆記錄的欄位,如「使用者 ID」。
- 外鍵(Foreign Key, FK):用來建立不同表格之間的關聯,如「訂單表」中的「使用者 ID」可以關聯到「使用者表」。
關聯式資料庫的優勢
使用關聯式資料庫的主要優勢包括:
- 結構化數據管理:透過表格儲存與關聯設計,使數據更有條理。
- 資料一致性與完整性:透過主鍵、外鍵與約束條件,確保數據的準確性。
- 強大的查詢能力:支援 SQL(Structured Query Language),可高效檢索與操作數據。
什麼是交易(Transaction)?
當應用程式與資料庫進行數據操作時,往往不只涉及單一表格或單一筆資料。
例如,在銀行轉帳時,系統需要同時扣除 A 帳戶的餘額並增加 B 帳戶的餘額,這種涉及多筆數據的操作就需要「交易(Transaction)」來確保一致性。
交易的概念
交易(Transaction) 是資料庫操作的一個邏輯單位,確保一組相關的操作要麼全部成功,要麼全部失敗。
這樣可確保資料不會因為系統故障或錯誤而變得不一致。
交易的 ACID 特性
為了確保數據的可靠性,交易需遵循 ACID 原則:
- A(Atomicity,原子性):所有操作要麼全部執行,要麼全部回滾,不會發生「只執行部分操作」的情況。
- C(Consistency,一致性):交易執行前後,數據的一致性必須維持,例如轉帳前後,銀行的總餘額不變。
- I(Isolation,隔離性):不同交易之間彼此獨立,避免互相影響導致數據錯誤。
- D(Durability,持久性):一旦交易提交,變更將永久存儲在資料庫中,不會因為系統崩潰而丟失。
交易的使用場景
- 銀行轉帳:確保扣款與入款同時發生,否則回滾。
- 電商訂單處理:確認付款成功後才建立訂單,否則取消。
- 庫存管理:確保減少庫存與訂單建立同步完成。
如何在 SQL 中使用交易?
在關聯式資料庫中,交易通常透過 SQL 來管理,以下是常見的交易控制語句:
交易的基本操作
BEGIN TRANSACTION; -- 開始交易
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1; -- 扣款
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2; -- 入款
COMMIT; -- 提交交易
若某一步驟失敗,可以回滾交易:
BEGIN TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
-- 模擬錯誤
ROLLBACK; -- 回滾交易
交易隔離級別(Transaction Isolation Levels)
在電商系統中,當多個顧客同時查詢、購買商品或進行付款時,資料庫需要確保數據的一致性,避免因為併發操作導致錯誤的結果。
例如,兩位顧客同時購買同一件商品,可能會導致超賣(Overselling),或者顧客查詢的價格與實際結帳時不同,影響購物體驗。
為了解決這些問題,資料庫提供了交易隔離級別(Transaction Isolation Levels),用來控制不同交易之間的影響。
交易隔離級別越高,能確保數據一致性的程度越高,但同時可能會降低系統效能。
因此,在不同的業務需求下,我們需要選擇適當的隔離級別來平衡數據一致性與系統效能。
接下來,我們會透過電商購物的情境來說明每個隔離級別的運作方式,幫助你理解不同的交易隔離級別如何影響查詢與交易結果。
READ UNCOMMITTED(未提交讀)
情境:顧客查詢商品庫存
問題:可能會發生「髒讀(Dirty Read)」,讀到未提交的數據,導致顧客看到錯誤的庫存資訊。
📌 例子:
- 顧客 A 查詢「iPhone 15」的庫存,系統顯示 10 台(交易開始)。
- 顧客 B 購買 3 台 iPhone,系統執行
UPDATE
語句,將庫存數量改為 7 台,但交易尚未提交(COMMIT)。 - 顧客 A 在未提交前再次查詢庫存,看到的是 7 台(因為讀到了還沒正式提交的數據)。
- 突然,顧客 B 取消訂單(ROLLBACK),庫存數量恢復為 10 台。
- 但此時,顧客 A 看到的是錯誤的資訊(7 台),而不是實際的 10 台。
這就是髒讀的問題,因為交易 A 讀到了未提交的變更,這些變更可能最終被取消,導致顧客 A 看到錯誤的資訊。
READ COMMITTED(已提交讀)
情境:顧客查詢商品價格
問題:可能會發生「不可重複讀(Non-Repeatable Read)」,即同一交易內的多次查詢結果不一致。
📌 例子:
- 顧客 A 查詢「MacBook Pro」的價格,顯示 $50,000(交易開始)。
- 商家 B 進行促銷活動,將「MacBook Pro」的價格改為 $45,000,並提交交易(COMMIT)。
- 顧客 A 在同一交易內再次查詢價格,這次顯示 $45,000。
這時,顧客 A 在同一交易內查詢兩次,卻得到了不同的價格,這就是「不可重複讀(Non-Repeatable Read)」,因為數據在交易期間發生了變更。
📌 影響:
- 如果顧客 A 在第一次查詢時決定購買,但在點擊「結帳」前價格已經變更,他可能會感到困惑或不滿。
- READ COMMITTED 隔離級別雖然能避免髒讀,但無法保證查詢結果在同一交易內保持一致。
REPEATABLE READ(可重複讀)
情境:顧客查詢商品是否有折扣
問題:REPEATABLE READ 確保交易期間查詢結果一致,避免「不可重複讀」,但仍可能發生「幻讀(Phantom Read)」,即交易內的數據總數可能變化。
📌 例子:
- 顧客 A 在電商平台上查詢「特價商品清單」,系統顯示 AirPods 和 iPad 是目前正在促銷的商品(交易開始)。
- 商家 在另一個交易中新增了一個新的促銷商品 MacBook Pro,並提交交易(COMMIT)。
- 顧客 A 在同一交易內再次查詢特價商品,系統仍然只顯示 AirPods 和 iPad,因為 REPEATABLE READ 保證相同的查詢結果不會變動。
- 顧客 A 完成結帳,系統生成訂單時,發現 MacBook Pro 其實也在特價清單內,但他在交易開始時並沒有看到這個商品。
📌 這就是「幻讀(Phantom Read)」的問題,因為交易開始時 MacBook Pro 並不在特價清單內,但在交易進行過程中,它被新增到資料庫,影響了交易的最終結果。
為什麼這是幻讀?
- 不可重複讀(Non-Repeatable Read) 指的是相同的查詢在同一交易內返回不同的結果(例如,顧客 A 查詢價格,第一次看到 $50,000,第二次變成 $45,000)。
- 幻讀(Phantom Read) 指的是查詢時原本沒有符合條件的數據,但在交易內卻突然多出了新的數據(例如,查詢特價商品時,原本沒有 MacBook Pro,但結帳時卻發現它也被加入特價清單)。
如何解決幻讀?
- 在 REPEATABLE READ 隔離級別下,顧客 A 看到的查詢結果會保持一致,但無法防止新商品被加入到促銷清單中。
- 如果要完全避免這種情況,就需要使用 SERIALIZABLE 隔離級別,這樣在顧客 A 的交易完成前,其他交易無法新增新的促銷商品,確保結果完全一致。
SERIALIZABLE(可串行化)
情境:秒殺活動的訂單處理
問題:確保交易彼此完全獨立,完全避免數據競爭,但可能降低效能。
📌 例子:
- 顧客 A 參加電商「限量秒殺」活動,查詢「PS5」的庫存,顯示 1 台(交易開始)。
- 顧客 B 也同時查詢「PS5」,看到的庫存也是 1 台(交易開始)。
- 顧客 B 先完成訂單並提交(COMMIT),這時庫存變為 0 台。
- 顧客 A 嘗試購買 PS5,但因為 SERIALIZABLE 隔離級別的保護,這筆交易必須等 B 完成後才能執行,當 A 嘗試購買時,系統發現庫存為 0,導致交易失敗。
📌 影響:
- 完全避免了競爭條件,確保 PS5 只會被「最早成功提交交易」的顧客買走,不會發生**超賣(Overselling)**的問題。
- 但由於交易必須排隊執行,如果有大量顧客同時購買,系統的效能可能會下降,導致顧客需要等待較長時間。
如何選擇適合的交易隔離級別?
隔離級別 | 解決的問題 | 可能發生的問題 | 適用場景 |
---|---|---|---|
READ UNCOMMITTED | 執行速度最快 | 可能讀到錯誤數據(髒讀) | 低一致性要求,如瀏覽不重要的數據 |
READ COMMITTED | 避免讀到未提交的數據 | 同一交易內可能查到不同結果(不可重複讀) | 一般電商應用,如商品查詢 |
REPEATABLE READ | 確保同一交易內的查詢結果一致 | 可能發生幻讀(Phantom Read) | 訂單管理,確保庫存和價格不變 |
SERIALIZABLE | 確保數據完全一致,不會發生競爭 | 效能最低,交易需要排隊執行 | 高價值交易,如限量秒殺、銀行轉帳 |
如何設定交易隔離級別?
不同的資料庫管理系統(如 MySQL、PostgreSQL、SQL Server)允許開發者手動設定交易的隔離級別。在 SQL 中,你可以使用以下語法來設定隔離級別:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
你可以將 SERIALIZABLE
改成其他級別,例如:
READ UNCOMMITTED
READ COMMITTED
REPEATABLE READ
SERIALIZABLE
該選擇哪種隔離級別?
- 如果你對資料一致性要求不高,且想要最快的執行速度 →
READ UNCOMMITTED
- 如果你想避免讀到未提交的錯誤數據(推薦一般應用使用) →
READ COMMITTED
- 如果你希望同一交易內,查詢的數據不會變動 →
REPEATABLE READ
- 如果你想確保數據完全一致,並且願意犧牲效能 →
SERIALIZABLE
在大多數應用中,READ COMMITTED
是一個良好的折衷方案,而 SERIALIZABLE
則適用於關鍵業務(如銀行交易)需要極高數據安全性的場景。
結論
關聯式資料庫是現代應用中最重要的數據存儲方式,而交易機制則確保數據在各種操作中維持一致性與可靠性。
透過學習交易的 ACID 特性與SQL 交易管理方式,可以讓我們更好地理解如何設計穩定的資料庫應用。
希望本篇文章能幫助你對關聯式資料庫與交易有更清晰的概念,若你想進一步學習,可以開始練習 SQL 操作,並嘗試在實際應用中實施交易機制!🚀