# 我所理解的 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 環繞的問題。