Home
Explore Uedu
Student Console
Register as Member/Login
(2) In future presentations of the research findings, in addition to the course project website and public presentations, your real name and personal information will not appear in this research report. If you are interested in the research results, we can provide you with an executive summary after the study is completed.
問卷中心
Teacher Console
Course Setup
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

0

Local AI

--

CISOSE26 本地 AI UG26
政治大學 AQI 18 29°C

AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

資料庫概念

資料庫概念:從搶票災難到 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 與並行控制〉的內容回答。可以問我「解釋某段」「舉個例子」「出題考我」,或反白文中段落後點下方「解釋選取段落」。