在 Spring Boot(或說 Spring Transaction)中,**交易隔離級別(Transaction Isolation Level)** 控制的是:
> 🔐「一個交易中可以看見其他交易尚未提交的資料的程度」。
---
## ✅ 支援的隔離級別(對應 JDBC 與 SQL 標準)
Spring Boot 使用 `org.springframework.transaction.annotation.Isolation` 列舉類別,對應標準 SQL 隔離級別:
| 隔離級別 | 說明 | 能防止的問題 |
| ------------------ | --------------------------------- | --------------------- |
| `READ_UNCOMMITTED` | 可讀取其他交易尚未提交的資料 | ❌ 幻讀<br>❌ 重複讀<br>❌ 髒讀 |
| `READ_COMMITTED` | 僅讀取已提交的資料(Oracle 預設) | ❌ 幻讀<br>❌ 重複讀<br>✅ 髒讀 |
| `REPEATABLE_READ` | 同一筆資料讀多次值不會變 | ❌ 幻讀<br>✅ 重複讀<br>✅ 髒讀 |
| `SERIALIZABLE` | 完全隔離,交易序列化執行 | ✅ 幻讀<br>✅ 重複讀<br>✅ 髒讀 |
| `DEFAULT` | 使用資料庫預設(Oracle 是 READ\_COMMITTED) | 取決於資料庫設定 |
---
## 🧪 三種典型問題與對應隔離級別
| 問題類型 | 說明 | 防止它的隔離級別 |
| ------------------------- | ----------------------- | ------------------ |
| 髒讀 Dirty Read | A 讀到 B 尚未提交的修改資料 | READ\_COMMITTED ↑ |
| 不可重複讀 Non-Repeatable Read | A 兩次讀資料,中間 B 修改並提交 | REPEATABLE\_READ ↑ |
| 幻讀 Phantom Read | A 查詢多筆資料,中間 B 插入新資料滿足條件 | SERIALIZABLE ↑ |
---
## ✅ 使用方式
```java
@Transactional(isolation = Isolation.SERIALIZABLE)
public void process() {
// 此交易以 SERIALIZABLE 隔離級別執行
}
```
或透過設定預設值:
```yaml
spring:
datasource:
url: ...
jpa:
properties:
hibernate.connection.isolation: 8 # 對應 SERIALIZABLE
```
Hibernate isolation 對應值如下(JDBC level):
| JDBC 等級 | 常數值 | 對應隔離級別 |
| ------- | ------------------ | ------ |
| 1 | `READ_UNCOMMITTED` | |
| 2 | `READ_COMMITTED` | |
| 4 | `REPEATABLE_READ` | |
| 8 | `SERIALIZABLE` | |
---
## ⚠️ 注意
* 使用高隔離級別會增加 **鎖衝突機率**、降低效能,適合需要一致性的情境(如:金流、票券、搶購)。
* 多數情況下配合悲觀鎖 (`@Lock(PESSIMISTIC_WRITE)`) + 預設隔離級別就能避免重複處理。
---
---
當我們在說資料庫的「**隔離級別**」時,其實是在控制「多個交易(transaction)併發執行時,互相影響的程度」。這會影響資料的一致性和交易的正確性。
以下是 **三種典型問題** 的詳細說明,及**各隔離級別**是否能避免這些問題:
---
## 🔸1. 髒讀(Dirty Read)
### ✅ 說明:
交易 A 讀取了交易 B 尚未 **提交** 的資料,若 B 最後 **回滾(rollback)**,A 讀到的資料就是錯的。
### 💡 範例:
1. 交易 B 對帳戶 A 寫入 `balance = 50`,但尚未提交。
2. 交易 A 讀到 `balance = 50`。
3. B 發現錯誤,回滾。
4. A 讀到的是「不存在」的資料。
### 🛡️ 哪些能防止:
| 隔離級別 | 能防止? |
| ------------------ | ---- |
| `READ_UNCOMMITTED` | ❌ 不行 |
| `READ_COMMITTED` | ✅ 可以 |
| `REPEATABLE_READ` | ✅ 可以 |
| `SERIALIZABLE` | ✅ 可以 |
---
## 🔸2. 不可重複讀(Non-Repeatable Read)
### ✅ 說明:
同一個交易中,兩次查詢同一筆資料,卻發現結果不同,因為另一個交易在中間更新並提交了資料。
### 💡 範例:
1. 交易 A 查詢帳戶 A `balance = 100`
2. 交易 B 將 `balance` 改為 `200` 並提交。
3. 交易 A 再查一次,發現 `balance = 200`
同一筆資料在同一交易中,讀取結果不一致。
### 🛡️ 哪些能防止:
| 隔離級別 | 能防止? |
| ------------------ | ---- |
| `READ_UNCOMMITTED` | ❌ 不行 |
| `READ_COMMITTED` | ❌ 不行 |
| `REPEATABLE_READ` | ✅ 可以 |
| `SERIALIZABLE` | ✅ 可以 |
---
## 🔸3. 幻讀(Phantom Read)
### ✅ 說明:
在同一個交易中,兩次查詢**滿足某條件的多筆資料集合**,結果不一致,因為其他交易在中間新增或刪除了符合條件的資料。
### 💡 範例:
1. 交易 A 查詢 `select * from orders where amount > 100`,共 5 筆
2. 交易 B 插入一筆 `amount = 150` 並提交
3. 交易 A 再查一次,變成 6 筆
資料不是重複更新,而是「整體結果的集合發生改變」,這就是「幻影」。
### 🛡️ 哪些能防止:
| 隔離級別 | 能防止? |
| ------------------ | ------------------------ |
| `READ_UNCOMMITTED` | ❌ 不行 |
| `READ_COMMITTED` | ❌ 不行 |
| `REPEATABLE_READ` | ❌ 不行(MySQL InnoDB 也不完全防) |
| `SERIALIZABLE` | ✅ 可以(以鎖表或範圍查詢鎖防止) |
---
## 🔄 總結比較表:
| 隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
| ----------------- | -- | ----- | -- |
| READ\_UNCOMMITTED | ❌ | ❌ | ❌ |
| READ\_COMMITTED | ✅ | ❌ | ❌ |
| REPEATABLE\_READ | ✅ | ✅ | ❌ |
| SERIALIZABLE | ✅ | ✅ | ✅ |
---
## ✅ 什麼時候該用哪一種?
| 場景 | 推薦隔離級別 | 補充說明 |
| -------------- | ------------------------- | --------- |
| 高併發搶券、購物下單 | `READ_COMMITTED` + 鎖/唯一約束 | 效能與正確性取平衡 |
| 資料分析(只讀) | `READ_COMMITTED` | 效能優先 |
| 銀行轉帳、帳務處理 | `SERIALIZABLE` 或手動加鎖 | 資料一致性最重要 |
| 嚴格防止同一查詢返回不同資料 | `REPEATABLE_READ` | 防止不可重複讀 |
---
如果你有具體的業務流程(例如:優惠券領取、庫存扣減、金流等),可以跟我說,我可以幫你一起選出最合適的隔離級別與輔助手段(例如是否加悲觀鎖、唯一索引、Redis 鎖等)。