如何使用redis實現分佈式鎖
爲什麼要使用分佈式鎖?場景?
涉及到重複提交或交易的地方
場景一:提交訂單
用戶購買商品,下單時,有時不小心連續點擊多次;
或者網絡不好,導致用戶以爲沒有提交,重複點擊提交按鈕;
網絡層面比如nginx的重發.
對於分佈式系統,提交訂單的n個請求可能會被不同的服務單體消費,
那麼就會生成多個相同(除了訂單號,其他購買信息完全一樣)的訂單,
後果:
- 產生了髒數據,影響了校驗,有時甚至會影響正常業務的執行;
- 前端用戶會發現產生了多個訂單,讓用戶迷茫,不知所措.
場景二:
有哪些解決方法呢?
使用其他方式實現分佈式鎖
參考: 分佈式系統後臺如何防止重複提交
使用redis
流程如下:
代碼如下:
/***
* 提交訂單
* @param model
* @param request
* @param response
* @return
*/
@ResponseBody
@RequestMapping(value = "/order/submit/json", produces = SystemHWUtil.RESPONSE_CONTENTTYPE_JSON_UTF)
public String jsonSubmitOrder2(Model model, HttpServletRequest request, HttpServletResponse response
, @RequestParam(required = false) Boolean del) {
//可以抽取出常量
String lockUniquePrefix = "lock";
Jedis jedis = getJedis();
//1. 獲取鎖
// key:"lock"+方法名,value:時間戳
//NX -- Only set the key if it does not already exist.
String key = lockUniquePrefix + Thread.currentThread().getStackTrace()[1].getMethodName();
//"OK":成功;null:失敗
String result = jedis.set(key, "aa", "NX", "EX"/*seconds*/, 1000);
Const.pool.returnResource(jedis);
boolean success = "OK".equals(result);
System.out.println("success :" + success);
System.out.println("result :" + result);
if (success) {
//2. 執行具體業務邏輯
//...
}
//3. 業務邏輯執行完成之後,釋放鎖
jedis = getJedis();
jedis.del(key);
Const.pool.returnResource(jedis);
return BaseResponseDto.put2("result", result).put("success", success).toJson();
}
爲什麼jedis.set方法中要使用"NX"呢? 因爲只有當key不存在時,操作纔會成功, 即key不存在時,jedis.set返回"OK",表示獲取鎖成功; key存在時,jedis.set返回null,表示獲取鎖失敗
爲什麼要使用redis
因爲關於鎖有兩個重要的操作:
- 獲取鎖;
- 釋放鎖.
在分佈式環境,必須保證這兩個操作是原子性的,
即不能把獲取鎖分爲兩步:先查詢,再add.
同時,獲取鎖時,能夠設置有效期.
分佈式鎖實現時要注意的問題
- 提供鎖的服務必須是一個唯一的服務,即負載均衡的n個服務單體訪問的是同一個服務;
- 能夠設置鎖的有效期,不能讓某個消費者永久地持有鎖;
- 能夠釋放鎖;
- 不同的業務邏輯競爭不同的鎖,必須下單和減庫存 使用不同的鎖.
redis 還能做什麼
redis除了可以實現分佈式鎖,還能作爲緩存服務器,
在實現需求中,我經常把一些容易變化的配置放在redis中, 這樣當產品經理需求變更時,我只需修改redis,即時生效,不用上線
參考:
https://my.oschina.net/huangweiindex/blog/1853160