2011年9月26日

SQL 交易



是所有資料庫系統的一個基本概念。 一次交易的要點就是它把多個步驟捆綁成了一個單一的不成功則成仁的操作。 其它並發的交易是看不到在這些步驟之間的中間狀態的,並且如果發生了一些問題, 導致該交易無法完成,那麼所有這些步驟都完全不會影響資料庫。
比如,假設一個銀行的資料庫包含各種客戶帳戶的餘額,以及每個分行的總餘額。 假設我們要記錄一次從 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 收到 100 塊不是 Alice 支付的錢, 也不希望 Alice 老是不花錢從 Bob 那裡拿到物品。我們需要保證:如果在操作的過程中出了差錯, 那麼所有這些步驟都不會發生效果。把這些更新組合成一個交易就給予我們這樣的保證。 交易被認為是原子的:從其它交易的角度來看,它要麼是全部發生,要麼完全不發生。
我們還需要保證:一旦一個交易完成並且得到資料庫系統的認可, 那麼它必須被真正永久地儲存,並且不會在隨後的崩潰中消失。 比如,如果我們記錄到了一個 Bob 撤單的動作, 那麼我們不希望僅僅在他走出銀行大門之後的一次崩潰就會導致對他的帳戶的扣減動作消失。 一個交易型資料庫保證一個交易所做的所有更新在交易發出完成響應之前都記錄到永久的儲存中(也就是磁盤)。
交易型資料庫的另外一個重要的性質和原子更新的概念關係密切: 當多個交易並發地執行的時候,那麼每個交易都不應看到其它交易所做的未完成的變化。 比如,如果一個交易正忙著計算所有分行的餘額總和, 那麼它不應該包括來自 Alice 的分行的扣帳和來自 Bob 分行的入帳,反之亦然。 所以交易必須是黑白分明的,不僅僅體現在它們在資料庫上產生的永久影響出發,而且體現在它們運轉時的自身的可讀性上。 一個打開的交易做的更新在它完成之前是其它交易無法看到的,而到提交的時候所有更新同時可見。
 PostgreSQL 裡,一個交易是透過把 SQL 命令用 BEGIN  COMMIT 命令包圍實現的。 因此我們的銀行交易實際上看起來像下面這樣

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



如果在該交易的過程中,我們決定不做提交(可能是我們剛發現 Alice 的餘額是負數), 那麼我們可以發出 ROLLBACK 命令而不是 COMMIT 命令,那麼到目前為止我們的所有更新都會被取消。
PostgreSQL 實際上把每個 SQL 語句當做在一個交易中執行的來看待。 如果您沒有發出 BEGIN 命令,那麼每個獨立的語句都有一個隱含的 BEGIN 和(如果成功的話) COMMIT 語句包圍在周圍。 一組包圍在 BEGIN COMMIT 語句中間的語句有時候被稱做交易
注意一些客戶庫自動發出 BEGIN  COMMIT, 因此您可能不需要特意請求就可以獲取交易的效果。查看您使用的接口的文件。
我們可以透過使用 savepoints 的方法,在一個交易裡更加精細地控制其中的語句。 保存點允許您有選擇性地拋棄交易中的某些部分,而提交其它剩下的。 在用 SAVEPOINT 定義了一個保存點後,如果需要,您可以使用 ROLLBACK TO 回滾到該保存點。 則該交易在定義保存點到回滾到它之間的所有資料庫更改都被拋棄,但是在保存點之前的修改將被保留。
在回滾到一個保存點之後,這個保存點仍然保存著其定義,所以您可以回滾到這個位置好幾次。 當然,如果您確信您不需要再次回滾到一個保存點,那麼您可以釋放它,這樣系統可以釋放一些資源。 要記住:釋放或者回滾到一個保存點都會自動釋放在其後定義的所有保存點。
所有這些都發生在一個交易內部,所以所有這些都不可能被其它交易會話看到。 當且僅當您提交了這個交易,這些提交了的動作才能以一個但願的方式被其它會話看到, 而回滾的動作完全不會再被看到。
還記得我們的銀行資料庫嗎?假設我們從 Alice 的帳戶上消費 $100.00, 然後給 Bob 的帳戶進行貸記加款,稍後我們發現我們應該給 Wally 的賬號貸記加款。 那麼我們可以像下面這樣的保存點來做:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
-- 呀!加錯錢了,應該用 Wally 的賬號
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Wally';
COMMIT;



這個例子當然是實在太簡單了,但是透過使用保存點,我們可以對交易有大量的控制。 並且,ROLLBACK TO是除了交易全部回滾,重新來過之外的唯一可用的, 用於重新控制一個因錯誤而被系統置於退出狀態下的交易的方法。

(轉譯自 PostgreSQL 中國 製作的簡體中文版本)


沒有留言:

ShareThis