Home
探索 Uedu
學生控制台
註冊會員/登入
研究知情同意中心
教師控制台
課程設定
支援與訊息
Uptime 數據

UeduGPTs

--

Jupyters

4

UG26 CISOSE26
臺北 AQI 46 · 臺中 AQI 28 · 臺南 AQI 24 · 高雄 AQI 33

AI 回覆桌面通知

AI 助教回覆完成時顯示桌面通知

聊天訊息通知

同學在討論區發送訊息時通知

聲音通知

每當有新通知時播放提示音

資料庫概念

資料庫概念:從搶票災難到 ACID 與並行控制

為什麼純檔案不夠用,DBMS 如何用交易、隔離等級與並行控制守住資料的正確性

當兩個人同時搶最後一張演唱會門票

想像一個熱門演唱會開賣的瞬間。系統裡只剩一張票,而此刻有上萬人同時按下「立即購買」。如果系統只是把座位資料存成一個文字檔,那麼可能發生這樣的慘劇:使用者 A 讀到「還剩 1 張」,使用者 B 幾乎同一毫秒也讀到「還剩 1 張」,於是兩個人都付了款、都收到「購票成功」,但實體座位只有一個。隔天兩位學生拿著票券到場,卻被告知座位重複——這就是典型的資料災難。

要避免這種事,我們需要的不只是「把資料存起來」,而是一整套能夠保證「資料正確、不會互相打架、就算斷電也不丟失」的機制。這套機制,就是資料庫(database)與資料庫管理系統(DBMS, Database Management System)。

資料庫與資料管理概念示意圖

為什麼不能只用檔案?

很多人初學程式時,會直接把資料寫進 .txt.csv 檔。在資料量小、單人使用時,這完全可行。但隨著系統長大,純檔案系統(file system)會暴露出一連串難以收拾的問題。

第一,資料冗餘與不一致(redundancy and inconsistency)。 假設你用三個檔案分別記錄訂單、客戶、出貨。同一位客戶的地址可能在三個檔案裡各存一份。當客戶搬家時,你必須記得同時改三個地方;只要漏改一個,系統就出現「同一個人有兩個地址」的矛盾。

第二,難以表達資料之間的關聯。 「哪些訂單屬於哪位客戶」這種關係,用平坦的檔案很難維護。每次查詢都要自己寫程式把多個檔案的內容對起來,既慢又容易出錯。

第三,並行存取(concurrent access)失控。 回到開頭的搶票情境:當多個程式同時讀寫同一個檔案,作業系統並不會自動幫你協調誰先誰後。兩個寫入可能交錯,導致檔案內容毀損。

第四,沒有交易與復原機制。 假設轉帳要做兩步:A 帳戶扣 1000、B 帳戶加 1000。如果第一步做完、寫第二步時程式當掉,錢就憑空消失了。純檔案沒有「全做或全不做」的保護。

第五,缺乏存取控制與查詢語言。 你無法輕易宣告「只有財務人員能看薪資欄位」,也沒有一個標準語言能讓你說「找出所有消費超過一萬元的客戶」。

DBMS 的存在,正是為了系統性地解決以上每一個痛點。

DBMS 到底扮演什麼角色

資料庫管理系統是介於「使用者/應用程式」與「實體儲存(磁碟)」之間的一層軟體。它的核心職責可以濃縮成幾件事:

  • 資料模型與結構化:以表格(table)、列(row)、欄(column)等結構組織資料,並透過綱要(schema)定義每個欄位的型別與限制。
  • 查詢語言:提供如 SQL(Structured Query Language)的宣告式語言,你只要說「我要什麼」,由 DBMS 決定「怎麼拿」。
  • 並行控制(concurrency control):協調多個使用者同時讀寫,避免互相干擾。
  • 交易管理(transaction management):保證一組操作的原子性與可靠性。
  • 復原與持久性(recovery and durability):即使系統當機、斷電,已確認的資料也不會遺失。
  • 存取控制與安全:管理權限,確保只有被授權者能讀寫對應資料。

值得強調的是「資料獨立性(data independence)」這個觀念:應用程式不需要知道資料實際上以什麼格式、存在磁碟的哪個位置。底層儲存方式改變時,上層程式幾乎不用改動。這讓系統能長期演進而不崩潰。

ACID:交易正確性的四道防線

DBMS 提供可靠保證的核心,是「交易(transaction)」這個概念。一筆交易是一組「要嘛全部成功、要嘛全部不算」的操作。衡量交易可靠性的標準,業界用 ACID 四個字母概括。

A — 原子性(Atomicity):交易內的所有操作視為不可分割的整體。轉帳的「扣款」與「入帳」要嘛都發生,要嘛都不發生。中途失敗時,DBMS 會把已做的部分復原(rollback)。

C — 一致性(Consistency):交易執行前後,資料庫都必須滿足所有預先定義的規則(如「帳戶餘額不可為負」「外鍵必須存在」)。交易把資料庫從一個合法狀態帶到另一個合法狀態,絕不會停在違規的中間狀態。

I — 隔離性(Isolation):多筆交易同時進行時,每一筆「感覺起來」都像是獨自在跑,不會看到別人做到一半的髒資料。這正是搶票情境的關鍵。

D — 持久性(Durability):一旦交易被確認(commit),它的結果就永久寫定。即使下一秒機房停電,重新開機後資料依然存在。

持久性是怎麼做到的:先寫日誌

持久性聽起來簡單,但磁碟寫入需要時間,斷電隨時可能發生。多數 DBMS 採用「預寫式日誌(WAL, Write-Ahead Logging)」:在真正更新資料頁之前,先把「我要做什麼變更」記到一個只能往後追加的日誌檔,並確保日誌落地。即使資料頁還沒寫完就當機,重開機時 DBMS 可以根據日誌「重做(redo)」已確認的交易、「回復(undo)」未完成的交易。這是「持久性」與「原子性」共同的實作基石。

動手看一個例子:兩個交易並行

讓我們用 SQL 把開頭的搶票情境寫清楚。假設有一張座位表:

CREATE TABLE seats (
    seat_id INT PRIMARY KEY,
    is_sold TINYINT DEFAULT 0
);
INSERT INTO seats VALUES (1, 0);  -- 第 1 號座位尚未售出

一筆「購買」交易長這樣:

BEGIN;                                    -- 交易開始
SELECT is_sold FROM seats WHERE seat_id = 1;   -- 讀到 0,可以買
UPDATE seats SET is_sold = 1 WHERE seat_id = 1; -- 標記為已售
COMMIT;                                   -- 確認

問題在於:如果沒有適當的隔離,兩筆交易可能像下表這樣交錯:

時間 交易 A(使用者甲) 交易 B(使用者乙)
t1 讀 is_sold → 0
t2 讀 is_sold → 0
t3 UPDATE → 1
t4 UPDATE → 1
t5 COMMIT(成功) COMMIT(也成功)

兩筆交易都讀到「未售出」,於是都認為自己買成功了——同一個座位賣了兩次。

正確的做法是讓 DBMS 在讀取時就鎖定該列,或使用更高的隔離等級。例如:

BEGIN;
SELECT is_sold FROM seats WHERE seat_id = 1 FOR UPDATE;  -- 鎖住這一列
-- 此時交易 B 想對同一列上鎖會被擋住、必須等待
UPDATE seats SET is_sold = 1 WHERE seat_id = 1;
COMMIT;  -- 釋放鎖;B 才繼續、讀到 is_sold = 1,於是知道已售出

FOR UPDATE 讓 DBMS 在交易 A 結束前阻擋其他交易動到這一列。隔離性把「同時搶票」變成了「井然有序的排隊」。

重點回顧

  • 純檔案系統的不足:冗餘與不一致、難表達關聯、並行失控、無交易復原、缺乏存取控制與查詢語言——這些正是 DBMS 要解決的問題。
  • DBMS 的角色:在應用程式與磁碟之間提供結構化、查詢語言、並行控制、交易管理、復原與安全,並帶來資料獨立性。
  • ACID 是交易可靠性的四道防線:原子性(全做或全不做)、一致性(永遠合法)、隔離性(彼此互不干擾)、持久性(確認後永不遺失)。
  • 持久性靠預寫式日誌(WAL) 實作:先寫日誌再改資料,當機後可 redo/undo 回到正確狀態。
  • 並行控制(如鎖定、FOR UPDATE)把同時存取變成有序操作,避免搶票式的資料競爭。

深入探討(研究所視角)

ACID 中的「隔離性」在實務上並非非黑即白,而是一條光譜。完全隔離(每筆交易彷彿獨佔資料庫)能保證最強的正確性,卻會嚴重犧牲並行效能。因此 SQL 標準定義了四個「交易隔離等級(transaction isolation level)」,讓系統在正確性與效能之間取捨。理解它們的方式,是看它們各自「容許哪些並行異常(anomaly)」。

三種經典異常為: - 髒讀(dirty read):讀到另一筆交易尚未 commit 的資料,而對方後來又 rollback。 - 不可重複讀(non-repeatable read):同一交易內兩次讀同一列,數值卻被別人改掉了。 - 幻讀(phantom read):同一交易內兩次以相同條件查詢,第二次卻多出(或少了)符合條件的列,因為別人插入/刪除了資料。

四個隔離等級與容許的異常對應如下:

隔離等級 髒讀 不可重複讀 幻讀
Read Uncommitted 可能 可能 可能
Read Committed 不會 可能 可能
Repeatable Read 不會 不會 可能
Serializable 不會 不會 不會

等級越高越安全,但鎖定範圍越大、並行度越低。Serializable 在概念上等價於「所有交易的最終結果,相當於某種把它們一筆接一筆依序執行的結果」——這就是「可序列化(serializability)」這個正確性黃金標準。

實作隔離的兩大流派值得對照:

悲觀並行控制(pessimistic concurrency control) 假設衝突常發生,因此先上鎖再動作,常以「兩階段鎖定(2PL, Two-Phase Locking)」實現——交易分為「擴張期(只取鎖)」與「收縮期(只放鎖)」,可數學證明達成可序列化。代價是鎖定可能引發死結(deadlock):交易 A 等 B 持有的鎖、B 又等 A,互相僵持。DBMS 需以「等待圖偵測」或「逾時」打破死結。

樂觀並行控制(optimistic concurrency control)多版本並行控制(MVCC, Multi-Version Concurrency Control) 則假設衝突少見。MVCC 為每次寫入保留一個資料版本,讀取交易可看到屬於自己時間點的「快照(snapshot)」,因此「讀不擋寫、寫不擋讀」,大幅提升並行度。PostgreSQL、MySQL InnoDB 等主流系統都以 MVCC 為核心。許多系統實際提供的 Snapshot Isolation 能擋住髒讀與不可重複讀,卻仍可能出現一種微妙的「寫偏斜(write skew)」異常,因此並不完全等同於 Serializable——這也是學術界「Serializable Snapshot Isolation」研究試圖修補的縫隙。

這些主題也是分散式系統的橋樑。當資料庫橫跨多台機器時,ACID 的保證遇上網路分割,便導向著名的 CAP 定理——在一致性(Consistency)、可用性(Availability)、分割容忍(Partition tolerance)三者間無法同時完全兼得;以及為跨節點交易設計的「兩階段提交(2PC, Two-Phase Commit)」協定。從單機的鎖與日誌,到分散式的共識與一致性模型,並行控制始終是資料管理領域最深也最迷人的核心問題之一。

AI 共讀助教正在陪你讀:資料庫概念:從搶票災難到 ACID 與並行控制
嗨!我是這篇文章的共讀助教,只根據〈資料庫概念:從搶票災難到 ACID 與並行控制〉的內容回答。可以問我「解釋某段」「舉個例子」「出題考我」,或反白文中段落後點下方「解釋選取段落」。