markdown-it
demo
Delete
Submit
clear
permalink
在 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 鎖等)。
html
source
debug
Fork me on GitHub