2025年9月25日: PostgreSQL 18 釋出!
支援的版本: 當前 (18) / 17 / 16 / 15 / 14 / 13
開發版本: devel
不支援的版本: 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2

3.4. 事務 #

事務是所有資料庫系統的基本概念。事務的核心思想是將多個步驟捆綁成一個單一的、要麼全做要麼全不做的操作。在步驟之間的中間狀態對其他併發事務是不可見的,如果發生任何導致事務無法完成的故障,那麼所有步驟都不會對資料庫產生任何影響。

例如,考慮一個銀行資料庫,其中包含各種客戶賬戶的餘額以及分行的總存款餘額。假設我們要記錄從愛麗絲賬戶到鮑勃賬戶的 100.00 美元的付款。極度簡化地說,SQL 命令可能如下所示:

UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
    WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');

這些命令的細節在此處並不重要;重要的是,要完成這個相當簡單的操作需要涉及多個獨立的更新。我們的銀行官員希望得到保證,要麼所有這些更新都發生,要麼都不發生。系統故障導致鮑勃收到 100.00 美元,而愛麗絲的賬戶未被扣款,這絕對是不可接受的。愛麗絲也不會長期保持滿意,如果她的賬戶被扣款而鮑勃的賬戶未被記入。我們需要保證,如果在操作過程中發生任何問題,那麼到目前為止已執行的任何步驟都不會生效。將這些更新分組到一個 事務 中可以提供此保證。一個事務被稱為 原子:從其他事務的角度來看,它要麼完全發生,要麼根本不發生。

我們還希望得到保證,一旦事務完成並被資料庫系統確認,它就已經被永久記錄,即使之後不久發生崩潰也不會丟失。例如,如果我們正在記錄鮑勃的現金提取,我們不希望有任何機會在他離開銀行後,他賬戶上的借記會因為崩潰而消失。事務性資料庫保證,在事務報告完成之前,由事務所做的所有更新都已記錄在永久儲存(即磁碟)中。

事務資料庫的另一個重要特性與原子更新的概念密切相關:當多個事務併發執行時,每個事務都不應該能夠看到其他事務尚未完成的更改。例如,如果一個事務正在忙於計算所有分行的總餘額,那麼它在看到愛麗絲分行的借記但沒有看到鮑勃分行的貸記,或者反之亦然,這是不可接受的。因此,事務不僅在其對資料庫的永久影響方面必須是“全有或全無”的,而且在其發生時的可見性方面也必須如此。未完成事務所做的更改在事務完成之前對其他事務是不可見的,一旦事務完成,所有更改將同時變得可見。

PostgreSQL 中,事務是透過將事務的 SQL 命令用 BEGINCOMMIT 命令包圍來設定的。所以我們的銀行交易實際上看起來是這樣的:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
-- etc etc
COMMIT;

如果在事務進行過程中,我們決定不提交(也許我們剛剛注意到愛麗絲的餘額變為負數),我們可以發出 ROLLBACK 命令而不是 COMMIT,並且到目前為止我們所有的更新都將被取消。

PostgreSQL 實際上將每個 SQL 語句都視為在事務中執行。如果您不發出 BEGIN 命令,那麼每個單獨的語句都會隱式地在其周圍包裝一個 BEGIN 和(如果成功)一個 COMMIT。一組被 BEGINCOMMIT 包圍的語句有時被稱為 事務塊

注意

一些客戶端庫會自動發出 BEGINCOMMIT 命令,因此您可能在不明確要求的情況下獲得事務塊的效果。請查閱您正在使用的介面的文件。

透過使用 儲存點,可以更精細地控制事務中的語句。儲存點允許您選擇性地放棄事務的某些部分,同時提交其餘部分。在用 SAVEPOINT 定義了儲存點之後,您可以根據需要用 ROLLBACK TO 回滾到該儲存點。在定義儲存點和回滾到該儲存點之間的事務資料庫更改將被丟棄,但早於儲存點的更改將被保留。

回滾到儲存點後,該儲存點仍然被定義,因此您可以多次回滾到它。相反,如果您確定不再需要回滾到某個特定的儲存點,則可以釋放它,以便系統可以釋放一些資源。請記住,釋放儲存點或回滾到儲存點都會自動釋放其後定義的所有儲存點。

所有這些都發生在事務塊內,因此它們對其他資料庫會話都不可見。當且僅當您提交事務塊時,提交的操作作為一個整體對其他會話可見,而被回滾的操作則完全不可見。

回想一下銀行資料庫,假設我們從愛麗絲的賬戶中扣除了 100.00 美元,並將 100.00 美元記入鮑勃的賬戶,後來才發現我們應該記入瓦利的賬戶。我們可以使用儲存點這樣做:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
-- oops ... forget that and use Wally's account
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Wally';
COMMIT;

當然,這個例子過於簡化了,但是透過使用儲存點,可以在事務塊中實現很多控制。此外,除非完全回滾並重新開始,否則 ROLLBACK TO 是恢復因系統錯誤而進入中止狀態的事務塊的唯一方法。

提交更正

如果您在文件中發現任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用 此表單 報告文件問題。