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