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