1、業界有哪些主流的分佈式鎖實現方案?
目前主流的有三種,如下:
- 基於數據庫實現
基於數據庫來做分佈式鎖的話,通常是採用數據庫的樂觀鎖或悲觀鎖來實現。
- 基於ZooKeeper實現
基於ZooKeeper,是採用它的臨時有序節點來實現的分佈式鎖。
- 基於Redis實現
基於Redis實現的鎖機制,主要是依賴redis自身的原子來實現
以上三種方式都可以實現分佈式鎖,如果併發量不大的話,直接採用數據庫就可以,如果高併發的話,就要考慮zookeeper或redis,但從高併發高性能角度考慮,基於 Redis 實現性能會更好;所以如何選擇,還是取決於業務需求。
2、下面介紹基於redis的普通分佈式鎖
SET key value [NX] [XX] [EX <seconds>] [PX [millseconds]]
以上set 代替了 setnx +expire 需要分2次執行命令操作的方式,保證了原子性
127.0.0.1:6379> set lock true NX px 600000
OK
127.0.0.1:6379> set lock true NX px 600000
(nil)
如果setnx 返回ok 說明拿到了鎖
如果setnx 返回 nil,說明拿鎖失敗,被其他線程佔用。
3、案例實戰:基於redis的分佈式鎖實現下訂單防止重複提交
@PostMapping(value = "/createOrder", produces = APPLICATION_JSON_UTF8_VALUE, consumes = APPLICATION_JSON_UTF8_VALUE)
public String createOrder(@RequestBody OrderDTO obj) {
//步驟1:先轉換爲唯一MD5
String json=JsonUtil.object2Json(obj);
String md5 = DigestUtils.md5DigestAsHex(json.getBytes()).toUpperCase();
//步驟2:把md5設置爲分佈式鎖的key
/**
* setIfAbsent 的作用就相當於 SET key value [NX] [XX] [EX <seconds>] [PX [millseconds]]
* 設置過期
*/
Boolean bo=stringRedisTemplate.opsForValue().setIfAbsent(md5,"1",60, TimeUnit.SECONDS);
if(bo){
// 加鎖成功
log.debug("{}拿鎖成功,開始處理業務",md5);
try {
//模擬10秒 業務處理
Thread.sleep(1000*10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//不要刪除,萬一業務處理時間很短,頁面上點了兩次,點第二次的時候,第一次已經處理完了,這樣就同一張單子處理了兩次,還是等他過期比較好
//stringRedisTemplate.delete(md5);
log.debug("{}拿鎖成功,結束處理業務",md5);
return "ok";
}else{
log.debug("{}拿鎖失敗",md5);
//拿不鎖,直接退出
return "請不要重複點擊!";
}
}