交易是所有資料庫系統的基本概念。 交易的重點是將多個步驟捆綁到單個、全有或全無的操作中。 步驟之間的中間狀態對其他並行交易不可見,如果發生任何阻止交易完成的故障,則所有步驟都不會影響資料庫。
例如,考慮一個銀行資料庫,其中包含各個客戶帳戶的餘額以及分行的存款總餘額。 假設我們要記錄從 Alice 的帳戶到 Bob 的帳戶的 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');
這些命令的細節在這裡並不重要;重要的是,要完成這個相當簡單的操作,需要進行幾個獨立的更新。 我們銀行的主管會希望確保所有這些更新都發生,或者一個都不發生。 系統故障導致 Bob 收到未從 Alice 扣款的 100.00 美元當然是不行的。 如果 Alice 被扣款而 Bob 沒有被記入,她也不會長期成為快樂的客戶。 我們需要保證,如果在操作過程中出現問題,到目前為止執行的任何步驟都不會生效。 將更新分組到交易中可以給我們這個保證。 據說交易是原子性的:從其他交易的角度來看,它要么完全發生,要么根本不發生。
我們還希望保證,一旦交易完成並被資料庫系統確認,它實際上已被永久記錄,即使之後立即發生崩潰也不會丟失。 例如,如果我們正在記錄 Bob 的現金提款,我們不希望在他走出銀行大門後,他的帳戶扣款有任何機會在崩潰中消失。 事務型資料庫保證交易進行的所有更新都會記錄在永久儲存(即磁碟上),然後才會報告交易完成。
事務型資料庫的另一個重要屬性與原子更新的概念密切相關:當多個交易同時運行時,每個交易都不應能夠看到其他交易的未完成更改。 例如,如果一個交易正忙於統計所有分行的餘額,那麼包括 Alice 分行的借方但不包括 Bob 分行的貸方,反之亦然,這是不行的。 因此,交易必須是全有或全無的,不僅在其對資料庫的永久影響方面,而且在它們發生時的能見度方面。 在交易完成之前,開放交易到目前為止所做的更新對其他交易不可見,交易完成後,所有更新會同時變為可見。
在 PostgreSQL 中,透過用 BEGIN
和 COMMIT
命令包圍交易的 SQL 命令來設定交易。 所以我們的銀行交易實際上看起來像
BEGIN; UPDATE accounts SET balance = balance - 100.00 WHERE name = 'Alice'; -- etc etc COMMIT;
如果在交易過程中,我們決定不提交(也許我們剛才注意到 Alice 的餘額變為負數),我們可以發出命令 ROLLBACK
而不是 COMMIT
,並且到目前為止我們所有的更新將被取消。
PostgreSQL 實際上將每個 SQL 語句都視為在交易中執行。 如果您不發出 BEGIN
命令,則每個單獨的語句都有一個隱式的 BEGIN
和(如果成功)COMMIT
包裝在它周圍。 由 BEGIN
和 COMMIT
包圍的一組語句有時稱為交易區塊。
某些用戶端程式庫會自動發出 BEGIN
和 COMMIT
命令,因此您可能會在沒有要求的情況下獲得交易區塊的效果。 檢查您正在使用的介面的文件。
可以使用儲存點以更精細的方式控制交易中的語句。 儲存點允許您選擇性地丟棄交易的某些部分,同時提交其餘部分。 在使用 SAVEPOINT
定義儲存點之後,如果需要,您可以使用 ROLLBACK TO
回滾到該儲存點。 定義儲存點和回滾到儲存點之間的所有交易資料庫變更都會被丟棄,但早於儲存點的變更會被保留。
回滾到儲存點後,它將繼續被定義,因此您可以多次回滾到它。 相反地,如果您確定您不需要再次回滾到特定的儲存點,則可以釋放它,以便系統可以釋放一些資源。 請記住,釋放或回滾到儲存點都會自動釋放之後定義的所有儲存點。
所有這些都發生在交易區塊內,因此其他資料庫會話都看不到它。 當您提交交易區塊時,如果有的話,已提交的動作將以一個單位的形式對其他會話可見,而回滾的動作永遠不會可見。
回想一下銀行資料庫,假設我們從 Alice 的帳戶扣款 $100.00,並將款項存入 Bob 的帳戶,但後來才發現應該存入 Wally 的帳戶。我們可以像這樣使用儲存點 (savepoints) 來完成:
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
是重新獲得因錯誤而被系統置於中止狀態的交易區塊控制權的唯一方法,除非完全回滾並重新開始。
如果您在文件中發現任何不正確、與您特定功能的使用經驗不符或需要進一步澄清的地方,請使用此表單來報告文件問題。