樂觀鎖——避免高並發的數據被多人修改


版本號機制

一般是在數據表中加上版本號字段 version,表示數據被修改的次數。當數據被修改時,這個字段值會加1。

舉個簡單的例子:假設帳戶信息表中有一個 version 字段,當前值為 1 ,而當前帳戶的余額( balance )為 100 。

  1. 操作員 A 此時准備將其讀出( version=1 ),並從其帳戶余額中扣除 50( 100-50 );
  2. 操作員 A 操作的過程中,操作員 B 也讀入此用戶信息( version=1 ),並從其帳戶余額中扣除 20 ( 100-20 );
  3. 操作員 A 完成修改工作,將數據版本號加1( version=2 ),連同帳戶扣除后余額( balance=50 ),提交到數據庫完成更新;
  4. 操作員 B 完成了操作,也將版本號加1( version=2 )試圖向數據庫提交數據( balance=80 ),但此時比對數據庫記錄版本發現,操作員 B 提交的數據版本號為 2 ,數據庫記錄的當前版本也為 2 ,不滿足 “提交版本必須大於記錄當前版本才能執行更新“ 的樂觀鎖策略。

因此,操作員 B 的提交被駁回。這樣,就避免了操作員 B 用基於 version=1 的舊數據修改,最終造成覆蓋操作員 A 操作結果的可能。

CAS 算法

compare and swap(比較與交換),是一種有名的無鎖算法。無鎖編程,即不使用鎖(沒有線程被阻塞)的情況下實現多線程之間的變量同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS 算法涉及到三個操作數:

  • 需要讀寫的內存值 V
  • 進行比較的值 A
  • 擬寫入的新值 B

當且僅當 V 的值等於 A 時,CAS 通過原子方式用新值 B 來更新 V 的值,否則不會執行任何操作(比較和替換是一個 native 原子操作)。一般情況下,這是一個自旋操作,即不斷的重試

 

上面介紹了2種辦法,下面說下項目中實操

文件標注結果保存,多人或多場景可以修改,並發時可能出現問題

 

當修改值時,使用樂觀鎖 + findAndModify的原子操作

  public Result updateResultInfo(Result result, JSONObject finalResult) {
        Query query = new Query();
        query.addCriteria(Criteria.where("userTaskId").is(result.getUserTaskId())
                .and("userTaskFileId").is(result.getUserTaskFileId())
                .and("isDeleted").is(0)
                .and("version").is(result.getVersion()));
        Update update = new Update();
        update.set("finalResult", finalResult);
        update.set("version", result.getVersion() + 1);
        update.set("stamp", result.getStamp());
        return resultMongoTemplate.findAndModify(query, update, Result.class);
    }

當新增數據時,可能插入數據時,已經被別人插入了。此時使用分布式鎖,redis鎖或zk鎖等都可以

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM