---
# System prepended metadata

title: '我所理解的 PostgreSQL 03: Session 與 Transaction 到底是什麼?'
tags: [PostgreSQL, 我所理解的 PostgreSQL]

---

# 我所理解的 PostgreSQL 03: Session 與 Transaction 到底是什麼?

{%hackmd @moment89/tXJIcGqmSI6_fJQp2hnJUA %}

## Session 和 Transaction 不是一樣的東西嗎?

Session (連線、會話) 和 Transaction (交易、事務) 很容易被混淆，以下是請 ChatGPT 提供的比喻。(我覺得比喻得很好，很具體🤣)

### 🌳「超商顧客」比喻法

:::success
Session = 你走進超商的整段時間
:::

想像你走進 7-11，從推門進來到離開，這段期間就是一個 Session。
在這段時間內，你可以逛店、買東西、喝咖啡、甚至什麼都不做。
對資料庫來說，一個 Session 就是「從你連線到資料庫，到你斷線為止」。

:::success
Transaction = 你去櫃台結帳的一次購物過程
:::

當你拿了東西去櫃台，從收銀員開始掃條碼到你刷卡付款，這個「完整結帳流程」就是一個 Transaction。
如果你中途說「啊，我不買了」，那就是 `ROLLBACK` (交易取消)。
如果你完成付款並拿到發票，就是 `COMMIT` (交易完成)。

![image](https://hackmd.io/_uploads/S1YVaL_Pee.png)

💡也就是說
* ==Session 是連線的基本單位==。
* ==Transaction 是為了確保資料將一系列步驟綑綁為一組全有或全無的操作單位。==
* 一個 Session 可以完全不使用 Transaction (例如單純連線查詢資料庫 Config)，也可以有多個 Transaction (開啟連線後提交多次交易)。

## 如何進行 Transaction

Transaction 的開啟與結束指令很單純。
[`BEGIN`](https://www.postgresql.org/docs/current/sql-begin.html) = 開啟 Transaction
[`COMMIT`](https://www.postgresql.org/docs/current/sql-commit.html) = 提交 Transaction
[`ROLLBACK`](https://www.postgresql.org/docs/9.4/sql-rollback.html) = 取消 Transaction

故一個 Transaction 的執行會像是
```sql
BEGIN;

-- Some SQL
CREATE ...;
INSERT ...;
SELECT ...;
UPDATE ...;

COMMIT; -- or ROLLBACK
```

在使用 pgAdmin 介面向 PostgreSQL 發送 SQL 指令時，它會自動幫我們執行 `BEGIN` 指令開啟 Transaction 再執行 SQL。
並且有 Auto Commit 功能，也就執行完所選的一個或多個 SQL 指令後自動發出 `COMMIT` 指令；Auto Rollback On Error 功能則是在執行一個或多個 SQL 指令期間發生錯誤時會自動發出 `ROLLBACK` 指令。

![image](https://hackmd.io/_uploads/Bk1NFDldgl.png)

## 從 pgAdmin 上可以看到哪些 Session 和 Transaction 資訊?

從左側導覽列點擊 Database，可在 Dashboard 介面的 State 分頁看到當前連線的 Sessions。
透過 Transaction start 欄位可以快速看到該 Session 當前是否在執行 Transaction 中。
而 State 欄位則是顯示該連線的 Backend 狀態，若 Backend Process 正在執行查詢會顯示 active。
(這裡的資料是來自系統表 [pg_stat_activity](https://www.postgresql.org/docs/current/monitoring-stats.html#MONITORING-PG-STAT-ACTIVITY-VIEW)。)

![image](https://hackmd.io/_uploads/H14JALg_xl.png)


若該 Session 的 Transaction 被阻塞，會在 Blocking PIDs 顯示是哪一個 Session PID 的 Transaction 阻塞了它，如下圖。

![image](https://hackmd.io/_uploads/SJ0YWDluex.png)


若一個 Transaction 執行過久，Dashbard 會亮橘色、紅色，如下圖。

![image](https://hackmd.io/_uploads/HkUJMweuxl.png)
![image](https://hackmd.io/_uploads/ryEFfwedlx.png)



## 從 pgAdmin 上可以看到哪些 Session 和 Transaction 資訊?

修改資料庫參數 log_line_prefix 輸出 %c、%v 會在每條 log 的開頭加上 Session ID 和 Virtual Transaction ID，此外可以搭配調整 log_statement 紀錄所需的 log。

(注意不是紀錄 %x Transaction ID，這個是紀錄 PostgreSQL MVCC 架構資料版控的 ID)

![image](https://hackmd.io/_uploads/B1sjmvedex.png)

我的 log 參數配置如下。[^備註]

```
logging_collector = on
log_statement = 'all'
log_line_prefix = '%m [%p] [%c] [%v]'
```

[^備註]: 有些參數調整後需要執行 `SELECT pg_reload_conf();` 或重啟以生效。

在資料庫開啟 Session，逐行執行以下幾行完成 2 次 Transaction。

```sql
begin;
select 1;
select 2;
select 3;
rollback;

begin;
select 4;
select 5;
select 6;
rollback;
```

則可以在 Log 看到以下內容。

![image](https://hackmd.io/_uploads/SJR1vPxdel.png)

## PostgreSQL 的 Session 和 Transaction 注意事項

### Session

1. 資料庫參數 `max_connections` 限制==整個 Database Cluster 的總連線數==。
2. ==一個 Session 只能連線單一 Database 做查詢==，即使是在同個 Database Cluster 上也不能跨 Database 查詢。
3. Session 都是單一個 backend process，每個 Process 都會占用一點 CPU，故==連線數和 CPU 用量正相關==。
4. Session 的每次建立與釋放都是成本。(故建議用 Pool 管理➡️[pgBouncer 筆記](https://hackmd.io/@moment89/pgbouncer-note))
5. 若 SQL 觸發平行查詢的條件，是在該 backend process 中啟用多個 worker 實作平行查詢。
6. Windows 環境可在工作管理員可以看到該連線的 backend process。 (用 pid 可以和 pgAdmin Dashboard 對照，但不要從工作管理員把 Process 殺了，會讓資料庫觸發保護機制自動把所有 Session 砍了進入 Crash Recovery。)

### Transaction

要==避免開啟 Transaction 後長時間不提交卡住資料庫資源==，例如
* 若交易中有對資料表的異動，如 Update、Delete，這會對影響的資料列上鎖，會 Block 其它要對該資料列異動的資料庫操作。
* 會阻塞 PostgreSQL 的自動維護工作 AutoVacuum，長時間會導致資料膨脹無法被清理，甚至導致 Transaction id 環繞的問題。