Java開發學習(四十九)----MyBatisPlus更新語句之樂觀鎖

1、概念

在講解樂觀鎖之前,我們還是先來分析下問題:

業務並發現象帶來的問題:秒殺

  • 假如有100個商品或者票在出售,爲了能保證每個商品或者票只能被一個人購買,如何保證不會出現超買或者重複賣

  • 對於這一類問題,其實有很多的解決方案可以使用

  • 第一個最先想到的就是鎖,鎖在一臺服務器中是可以解決的,但是如果在多臺服務器下鎖就沒有辦法控制,比如12306有兩臺服務器在進行賣票,在兩臺服務器上都添加鎖的話,那也有可能會導致在同一時刻有兩個線程在進行賣票,還是會出現併發問題

  • 我們接下來介紹的這種方式是針對於小型企業的解決方案,因爲數據庫本身的性能就是個瓶頸,如果對其併發量超過2000以上的就需要考慮其他的解決方案了。

簡單來說,樂觀鎖主要解決的問題是當要更新一條記錄的時候,希望這條記錄沒有被別人更新。

2、實現思路

樂觀鎖的實現方式:

  • 數據庫表中添加version列,比如默認值給1

  • 第一個線程要修改數據之前,取出記錄時,獲取當前數據庫中的version=1

  • 第二個線程要修改數據之前,取出記錄時,獲取當前數據庫中的version=1

  • 第一個線程執行更新時,set version = newVersion where version = oldVersion

    • newVersion = version+1 [2]

    • oldVersion = version [1]

  • 第二個線程執行更新時,set version = newVersion where version = oldVersion

    • newVersion = version+1 [2]

    • oldVersion = version [1]

  • 假如這兩個線程都來更新數據,第一個和第二個線程都可能先執行

    • 假如第一個線程先執行更新,會把version改爲2,

    • 第二個線程再更新的時候,set version = 2 where version = 1,此時數據庫表的數據version已經爲2,所以第二個線程會修改失敗

    • 假如第二個線程先執行更新,會把version改爲2,

    • 第一個線程再更新的時候,set version = 2 where version = 1,此時數據庫表的數據version已經爲2,所以第一個線程會修改失敗

    • 不管誰先執行都會確保只能有一個線程更新數據,這就是MybatisPlus提供的樂觀鎖的實現原理分析。

上面所說的步驟具體該如何實現呢?

3、實現步驟

分析完步驟後,具體的實現步驟如下:

步驟1:數據庫表添加列

列名可以任意,比如使用version,給列設置默認值爲1

步驟2:在模型類中添加對應的屬性

根據添加的字段列名,在模型類中添加對應的屬性值

@Data
//@TableName("tbl_user") 可以不寫是因爲配置了全局配置
public class User {
    @TableId(type = IdType.ASSIGN_UUID)
    private String id;
    private String name;
    @TableField(value="pwd",select=false)
    private String password;
    private Integer age;
    private String tel;
    @TableField(exist=false)
    private Integer online;
    private Integer deleted;
    @Version
    private Integer version;
}
步驟3:添加樂觀鎖的攔截器
@Configuration
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mpInterceptor() {
        //1.定義MybatisPlus攔截器
        MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
        //2.添加樂觀鎖攔截器
        mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mpInterceptor;
    }
}
步驟4:執行更新操作

添加version數據

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testUpdate(){
        User user = new User();
        user.setId(3L);
        user.setName("Jock666");
        user.setVersion(1);
        userDao.updateById(user);
    }
}

你會發現,我們傳遞的是1,MybatisPlus會將1進行加1,然後,更新回到數據庫表中。

所以要想實現樂觀鎖,首先第一步應該是拿到表中的version,然後拿version當條件在將version加1更新回到數據庫表中,所以我們在查詢的時候,需要對其進行查詢

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testUpdate(){
        //1.先通過要修改的數據id將當前數據查詢出來
        User user = userDao.selectById(3L);
        //2.將要修改的屬性逐一設置進去
        user.setName("Jock888");
        userDao.updateById(user);
    }
}

大概分析完樂觀鎖的實現步驟以後,我們來模擬一種加鎖的情況,看看能不能實現多個人修改同一個數據的時候,只能有一個人修改成功。

@SpringBootTest
class Mybatisplus03DqlApplicationTests {
​
    @Autowired
    private UserDao userDao;
    
    @Test
    void testUpdate(){
       //1.先通過要修改的數據id將當前數據查詢出來
        User user = userDao.selectById(3L);     //version=3
        User user2 = userDao.selectById(3L);    //version=3
        user2.setName("Jock aaa");
        userDao.updateById(user2);              //version=>4
        user.setName("Jock bbb");
        userDao.updateById(user);               //verion=3?條件還成立嗎?
    }
}

運行程序,分析結果:

樂觀鎖就已經實現完成了,如果對於上面的這些步驟記不住咋辦呢?

參考官方文檔來實現:https://baomidou.com/pages/0d93c0/#optimisticlockerinnerinterceptor

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章