首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

資料庫併發控制:Spring @Transactional 配合樂觀鎖/悲觀鎖解決超賣問題

     編輯:程式設計綜合問答

場景

表 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);
}

若業務邏輯複雜需樂觀鎖

  1. 表加欄位 version INT DEFAULT 0。
  2. 實體類加 @Version Long version; (JPA) 或手動檢查。
  3. Service 方法:
@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("版本衝突,請重試");
}

Spring 事務注意事項

  • @Transactional 僅管理事務邊界(提交/回滾),不提供鎖機制。
  • 傳播行為 REQUIRED(預設)確保同一事務上下文。
  • 隔離級別 READ_COMMITTED 通常足夠,嚴格一致性可用 SERIALIZABLE(效能代價大)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved