?
本文檔使用 php中文網手冊 發(fā)布
事務是所有數(shù)據(jù)庫系統(tǒng)的一個基本概念。 一次事務的要點就是把多個步驟捆綁成一個單一的、不成功則成仁的操作。 其它并發(fā)的事務是看不到在這些步驟之間的中間狀態(tài)的, 并且如果發(fā)生了一些問題,導致該事務無法完成, 那么所有這些步驟都完全不會影響數(shù)據(jù)庫。
比如,假設一個銀行的數(shù)據(jù)庫包含各種客戶帳戶的余額,以及每個分行的總余額。 假設我們要記錄一次從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');
這些命令的細節(jié)在這兒并不重要; 重要的是這里牽涉到了好幾個獨立的更新來完成這個相當簡單的操作。 銀行官員會希望要么所有這些更新全部生效,要么全部不起作用。 我們當然不希望一次系統(tǒng)崩潰就導致Bob收到100塊不是Alice支付的錢, 也不希望Alice老是不花錢從Bob那里拿到物品。 我們需要保證:如果在操作的過程中出了差錯,那么所有這些步驟都不會發(fā)生效果。 把這些更新組合成一個事務 就給予我們這樣的保證。 事務被認為是原子的:從其它事務的角度來看, 它要么是全部發(fā)生,要么完全不發(fā)生。
我們還需要保證: 一旦一個事務完成并且得到數(shù)據(jù)庫系統(tǒng)的認可,那么它必須被真正永久地存儲, 并且不會在隨后的崩潰中消失。 比如,如果我們記錄到了一個Bob撤單的動作, 那么我們不希望僅僅在他走出銀行大門之后的一次崩潰就會導致對他的帳戶的扣減動作消失。 一個事務型數(shù)據(jù)庫保證一個事務所做的所有更新在事務發(fā)出完成響應之前 都記錄到永久的存儲中(也就是磁盤)。
事務型數(shù)據(jù)庫的另外一個重要的性質和原子更新的概念關系密切: 當多個事務并發(fā)地運行的時候,每個事務都不應看到其它事務所做的未完成的變化。 比如,如果一個事務正忙著計算所有分行的余額總和, 那么它不應該包括來自Alice的分行的扣帳和來自Bob分行的入帳,反之亦然。 所以事務必須是黑白分明的,不僅僅體現(xiàn)在它們在數(shù)據(jù)庫上產生的永久影響出發(fā), 而且體現(xiàn)在它們運轉時的自身的可視性上。 一個打開的事務做的更新在它完成之前是其它事務無法看到的,而到提交的時候所有更新同時可見。
在PostgreSQL里, 一個事務是通過把SQL命令用BEGIN和COMMIT命令包圍實現(xiàn)的。 因此我們的銀行事務實際上看起來像下面這樣:
BEGIN; UPDATE accounts SET balance = balance - 100.00 WHERE name = 'Alice'; -- 等等 COMMIT;
如果在該事務的過程中,我們決定不做提交(可能是我們剛發(fā)現(xiàn)Alice的余額是負數(shù)), 那么我們可以發(fā)出ROLLBACK而不是COMMIT命令, 那么到目前為止我們的所有更新都會被取消。
PostgreSQL實際上把每個SQL語句當做在一個事務中執(zhí)行來看待。 如果你沒有發(fā)出BEGIN命令, 那么每個獨立的語句都被一個隱含的BEGIN和(如果成功的話)COMMIT包圍。 一組包圍在BEGIN和COMMIT之間的語句 有時候被稱做事務塊。
Note: 一些客戶庫自動發(fā)出BEGIN和COMMIT, 因此你可能不需要特意請求就可以獲得事務塊的效果。查看你使用的接口的文檔。
我們可以通過使用保存點的方法,在一個事務里更加精細地控制其中的語句。 保存點允許你選擇性地拋棄事務中的某些部分,而提交剩下的部分。 在用SAVEPOINT定義了一個保存點后,如果需要, 你可以使用ROLLBACK TO回滾到該保存點。 則該事務在定義保存點到ROLLBACK TO之間的所有數(shù)據(jù)庫更改都被拋棄, 但是在保存點之前的修改將被保留。
在回滾到一個保存點之后,這個保存點仍然保存著其定義, 所以你可以回滾到這個位置好幾次。當然,如果你確信你不需要再次回滾到一個保存點, 那么你可以釋放它,這樣系統(tǒng)可以釋放一些資源。 要記?。横尫呕蛘呋貪L到一個保存點都會自動釋放在其后定義的所有保存點。
所有這些都發(fā)生在一個事務塊內部,所以所有這些都不可能被其它事務會話看到。 當且僅當你提交了這個事務塊,這些提交了的動作才能以一個單元的方式被其它會話看到, 而回滾的動作完全不會再被看到。
記得我們的銀行數(shù)據(jù)庫嗎? 假設我們從Alice的帳戶上消費$100.00, 然后給Bob的帳戶進行加款,稍后我們發(fā)現(xiàn)我們應該給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'; -- 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是除了事務全部回滾,重新來過之外, 唯一可以用于重新控制一個因錯誤而被系統(tǒng)置于退出狀態(tài)事務的方法。