表 A 欄位 b,多用戶並發讀取→修改→寫回,導致更新丟失(用戶 C 讀取舊值,用戶 D 連續更新兩次,C 寫回覆蓋 D 的結果)。
| 方案 | 原理 | 優點 | 缺點 |
|---|---|---|---|
| 悲觀鎖 (SELECT ... FOR UPDATE) | 讀取時加排他鎖,阻塞其他事務讀寫 | 強一致性、實現簡單 | 併發效能低、易死鎖 |
| 樂觀鎖 (版本號/時間戳) | 更新時檢查版本號,不匹配則重試 | 無鎖、高併發效能好 | 衝突頻繁時重試開銷大 |
| 原子操作 (UPDATE ... SET b = b + 1) | 單條 SQL 完成讀改寫,資料庫保證原子性 | 最簡單、效能最高、無業務鎖 | 僅適用簡單增減場景 |
@Transactional
public void incrementB(Long id) {
// 單條 SQL,資料庫引擎層面原子化,無需應用層鎖
jdbcTemplate.update("UPDATE table_a SET b = b + 1 WHERE id = ?", id);
}
version INT DEFAULT 0。@Version Long version; (JPA) 或手動檢查。@Transactional
public void updateWithOptimisticLock(Entity entity) {
int updated = jdbcTemplate.update(
"UPDATE table_a SET b = ?, version = version + 1 WHERE id = ? AND version = ?",
entity.getB(), entity.getId(), entity.getVersion()
);
if (updated == 0) throw new OptimisticLockingFailureException("版本衝突,請重試");
}
@Transactional 僅管理事務邊界(提交/回滾),不提供鎖機制。REQUIRED(預設)確保同一事務上下文。READ_COMMITTED 通常足夠,嚴格一致性可用 SERIALIZABLE(效能代價大)。