---
# System prepended metadata

title: DB Transaction筆記
tags: [Database, Transaction]

---

# DB Transaction筆記
###### tags: `Database` `Transaction`
## 目錄

[TOC]
## 認識Transaction
Transaction(中文稱為交易，有些地方稱事務)是為了確保程式在處理的過程中只能全數成功才會正式寫到DB中，因此只會有兩種結果，要麼全部成功，要麼全部失敗。

這樣講好像有點模糊，舉個沒Transaction會GG的例子：
- 使用網銀轉帳時，如果網銀沒有使用Transaction，轉錢的過程中突然被中斷了！那麼，當你的帳戶被扣款時，對方可能還沒有收到錢。這時你一定氣的跳腳🤬，你的錢可能已經被扣走了，但對方還沒有收到錢。

因此，資料庫系統可以在Transaction開始後，只要沒有確認成功`Commit`，就會回到交易開始前的狀態`Rollback`。
當程式中出現錯誤並需要全盤回覆原狀時，我們可以使用Transaction來保證資料正確性。

## Transaction ACID特性

### 原子性(Atomicity)
將整個交易啟動時到失敗或是成功時才會結束交易，因此這整段交易過程視為一個原子，是一個不可分割的邏輯單位。

### 一致性(Consistency)
交易前後保持一致性，因此只要定義好Transaction過程重要做什麼，就要確保前後是要符合預期結果，來確保Transaction前後資料庫完整性沒有被破壞。

### 隔離性(Isolation)
Transaction過程中不能被任何別的Transaction影響。
當同一時間有兩筆資料同時進行交易，這兩筆交易的運作獨立，不會互相干擾防止不一致的結果。

### 永久性(Durability)
一旦資料修改完成，變永久存在。


## Transaction併發錯誤

就算前面說了這些Transaction的特性，但是在某些高併發狀況下，可能產生競爭危害(Race Condition)，仍然會造成一些不可預期的錯誤

接下來都以兩次Transaction為例，T1為先開始的交易，T2為後開始的交易
### 髒讀Dirty Read

:::warning
交易(T1)讀到了交易(T2)尚未Commit的資料
:::
可以從下圖中看到T2在更新產品數量時發生`Rollback`，因此導致T1先前讀到的數量是錯誤的
![](https://i.imgur.com/LKzDMuN.png)

### 不可重複讀Non-repeatable read
:::warning
在交易(T1)過程中，因"另一個"交易(T2)提交了Commit，當次的交易(T1)前後查詢得到不同結果
:::
也就是在兩次查詢之間，相同的資料可能會出現不同的結果，這個狀況會導致Transaction違反前面所說的一致性
![](https://i.imgur.com/fQMG9gF.png)

### 幻讀Phantom Read
:::warning
一個交易(T1)過程中前後兩次查詢的資料總數不同，因為"另一個"交易(T2)使用INSERT或DELETE導致
:::
![](https://i.imgur.com/z23tSFz.png)

## 資料庫隔離層級
隔離層級聽起來很複雜，可以把隔離層級當作水龍頭的開關，利用開關大小來控制水流的量，如果開的很大的話，水流得自然快(效能好)，但就容易發生不可控制的狀況，反之亦然

在資料庫中就是利用隔離層級來做到解決一致性的難題，這些隔離機制是利用`鎖定`資源的概念實現的，另外實做上可能會搭配Queues的機制，不過在高併發的狀況下，Server能不能忍受這些流量也是需要考量的因素之一

![](https://i.imgur.com/eD60H5T.png)

### Read Uncommitted
最寬鬆的隔離層級，如字面上說的，可以讀到其他Transaction尚未`Commit`的資料，可能造成前一節中所有錯誤「髒讀、不可重複讀、幻讀」

### Read Committed
這個層級允許讀取已經`Commit`的資料，但是有可能發生「不可重複讀、幻讀」的狀況

### Repeatable Read
在同一次Transaction的狀況下，資料庫只會讓讀取結果相同，這樣避免了不可重複讀的狀況，但仍能產生「幻讀」的結果

### Serializable
最為嚴格的一個隔離層級，讓所有Transaction序列化執行，並且鎖定資源，確保相同時間只有一個Transaction在執行，用這個方法來避免所有發生的問題，效能最差，也最容易導致`Deadlock`問題


### 補充: Deadlock
前面有提到這個詞就稍微說明一下，在併發狀況下Transaction互相鎖住對方的資源時就會造成Deadlock，這個時候程式就會進入死循環，直到被強制終止或是超時才會Rollback並且釋放資源
![](https://i.imgur.com/MgKxy8n.png)

## 結論
- Transaction在高併發狀況下會產生錯誤讀取的狀況
- 在高併發的狀況下，Transaction容易產生錯誤讀取的狀況。為了避免這種狀況，可以選擇使用高隔離層級，但這樣做會對效能產生影響，因此在實際應用時，需要根據當下的情境來做取捨。

## 參考來源
[IT鐵人賽 前端工程師一起來種一棵後端技能樹吧！ Day16~19](https://ithelp.ithome.com.tw/users/20113277/ironman/2937)
    -　Database Transaction & ACID
    -　Transaction 併發錯誤與隔離層級
    
[RDBMS - 錯誤讀取現象及交易隔離層級原理介紹與舉例](https://blog.kennycoder.io/2020/02/09/RDBMS-%E9%8C%AF%E8%AA%A4%E8%AE%80%E5%8F%96%E7%8F%BE%E8%B1%A1%E5%8F%8A%E4%BA%A4%E6%98%93%E9%9A%94%E9%9B%A2%E5%B1%A4%E7%B4%9A%E5%8E%9F%E7%90%86%E4%BB%8B%E7%B4%B9%E8%88%87%E8%88%89%E4%BE%8B/)
