# 3.4. 交易安全

交易（Transaction），是所有資料庫的基礎概念。基本上來說，一個交易指的是，一系列的執行步驟包裹在一起，其結果只有全部成功或全部失敗兩種情況的操作行為。而其即時的執行狀態，對於其他同時在進行的交易而言，相互之間都是不可見的。如果在執行過程中產生了錯誤而造成整個交易無法完成，那麼所有的指令都不會對資料庫原來的內容產生影響。

舉例來說，某個銀行資料庫存放著各個客戶的存款資訊，也存放著分行的存款總額資訊。假設我們想要轉帳 $100.00，從 Alice 的帳號轉到 Bob 的帳戶。可以很直觀地依敘述，直接以下列指令執行：

```
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.00，但 Alice 卻沒有轉出金額，就不是應該發生的事。又或是 Alice 轉出了現金，而 Bob 卻沒有轉入金額，她也不會是開心的客戶。我們需要具有保證交易安全的方法，也就是如果在執行過程中，有部份出了錯，那麼即使是已經執行的部份，也不會對資料庫產生影響。把這些更新資料的指令，包裝在一個交易之中，就是這個保證交易安全的方法。這樣的交易稱作為 atomic：從其他的交易的角度來看，整個行為只有完全執行，亦或是什麼都沒有做，兩種結果而已。

我們也希望有某個保證是，一旦某個交易被完成了，那麼會由資料庫系統發出通知，使它確實是永久性的資料，即使發生短暫的當機之後，資料也不會遺失。舉例來說，如果我們正在進行 Bob 的提款系統操作行為，在他走出銀行大門之後，我們不要有任何可能性使他的提款記錄消失。一個具備交易安全的資料庫，會將這裡交易裡的更新行為，在它們被回報完成之前，都記錄在長效型儲存裝置上（也就是磁碟機）。

交易安全資料庫的另一個重要性質是， atomic update 的概念：當多個交易同時在進行時，每一個交易都不能夠看到其他交易未完成交易的資料狀態。舉個例子，如果某個交易正在進行總計所有分行的餘額，它不會只包含 Alice 的分行的提款，或不計算 Bob 的分行的存款，反之亦然。所以交易必須是全有全無的結果，而不只是資料庫資料的永久性，還包含了交易執行過程的可視性。一個未完成的交易直到完全完成之前，其間資料的改變，對其他的交易而言都看不見；而當交易完成的同時，資料的改變也同時全部呈現出來。

在 PostgreSQL 中，所謂的交易，是以 SQL 的 BEGIN 及 COMMIT 兩個指令相夾的過程。\
所以我們前述的銀行交易實際上會像這樣：

```
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）。交易儲存點允許你可以選擇性地取消部份交易，而只成交剩下的部份。使用 SAVEPOINT 指令定義一個交易儲存點之後，你可以使用 ROLLBACK，回復該交易狀態到交易儲存點。所有在交易儲存點之後所造成的資料庫變更，都會被回復，但交易儲存點之前的變更會暫時留存。

在回復到交易儲存點之後，它仍然可以繼續進行，而你可以多次回到該儲存點。相反地，如果你確定你不要再回復到某個特定的交易儲存點時，它也可以被釋放出來，系統資源也可以獲得舒解。記得，釋放或回復到一個交易儲存點時，將會自動釋放所有在那之後的交易儲存點。

所有這些過程都發生在交易區塊之中，所有沒有任何改變會讓其他資料庫連線所發現。當你確認完成了交易區塊的時候，完成交易的動作就會讓其他的連線知道，也能發現資料的改變；同時，回復的動作也會再也無法執行了。

記得這個銀行的資料庫，假設我們從 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';
-- 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 是唯一能夠控制交易區塊執行流程的方式，當系統產生錯誤時，可以縮小回復的範圍，而不是只能全部回復再執行。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.postgresql.tw/tutorial/advanced-features/transactions.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
