事務是所有資料庫系統的基本概念。事務的核心思想是將多個步驟捆綁成一個單一的、要麼全做要麼全不做的操作。在步驟之間的中間狀態對其他併發事務是不可見的,如果發生任何導致事務無法完成的故障,那麼所有步驟都不會對資料庫產生任何影響。
例如,考慮一個銀行資料庫,其中包含各種客戶賬戶的餘額以及分行的總存款餘額。假設我們要記錄從愛麗絲賬戶到鮑勃賬戶的 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 命令用 BEGIN
和 COMMIT
命令包圍來設定的。所以我們的銀行交易實際上看起來是這樣的:
BEGIN; UPDATE accounts SET balance = balance - 100.00 WHERE name = 'Alice'; -- etc etc COMMIT;
如果在事務進行過程中,我們決定不提交(也許我們剛剛注意到愛麗絲的餘額變為負數),我們可以發出 ROLLBACK
命令而不是 COMMIT
,並且到目前為止我們所有的更新都將被取消。
PostgreSQL 實際上將每個 SQL 語句都視為在事務中執行。如果您不發出 BEGIN
命令,那麼每個單獨的語句都會隱式地在其周圍包裝一個 BEGIN
和(如果成功)一個 COMMIT
。一組被 BEGIN
和 COMMIT
包圍的語句有時被稱為 事務塊。
一些客戶端庫會自動發出 BEGIN
和 COMMIT
命令,因此您可能在不明確要求的情況下獲得事務塊的效果。請查閱您正在使用的介面的文件。
透過使用 儲存點,可以更精細地控制事務中的語句。儲存點允許您選擇性地放棄事務的某些部分,同時提交其餘部分。在用 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
是恢復因系統錯誤而進入中止狀態的事務塊的唯一方法。
如果您在文件中發現任何不正確、與您對特定功能的體驗不符或需要進一步澄清的內容,請使用 此表單 報告文件問題。