Redis分布式鎖你會(huì)了嗎?
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
66套java從入門到精通實(shí)戰(zhàn)課程分享?
一、前言
這里我簡(jiǎn)單說(shuō)一下,代碼可能沒(méi)那么完美。希望各位大佬可以多多指點(diǎn)!!!要是有測(cè)試能力的兄弟可以幫我測(cè)一下,我這個(gè)分段加鎖我是全部都在Redis中實(shí)現(xiàn)主要用到的技術(shù)就是Lua腳本。
二、建議
建議搭配消息隊(duì)列使用,防止一下進(jìn)來(lái)太多請(qǐng)求。達(dá)到一個(gè)削峰的效果。
三、簡(jiǎn)述
嘗試加鎖:首先當(dāng)一個(gè)請(qǐng)求發(fā)送過(guò)來(lái)了,嘗試獲取分段鎖得時(shí)候會(huì)查出所有的分段倉(cāng)庫(kù)的key并嘗試創(chuàng)建標(biāo)識(shí)如果成功返回倉(cāng)庫(kù)的KEY不成功就進(jìn)行下個(gè)倉(cāng)庫(kù)KEY的如果成功了會(huì)使用倉(cāng)庫(kù)的KEY創(chuàng)建一個(gè)標(biāo)識(shí)來(lái)鎖定這個(gè)庫(kù)存并添加過(guò)期時(shí)間然后返回一個(gè) 倉(cāng)庫(kù)的KEY。
解鎖:根據(jù)之前那個(gè) 倉(cāng)庫(kù)的KEY 查出用戶唯一標(biāo)識(shí)然后和我們解鎖傳入的用戶id進(jìn)行比較看看是否相對(duì)再?zèng)Q定是否釋放這個(gè)鎖,釋放鎖的時(shí)候我們會(huì)檢查庫(kù)存如果為0那么順便刪除庫(kù)存提高下次鎖獲取的效率。


四、流程圖

五、核心代碼
Lua腳本部分為了方便各位大神查看我單獨(dú)寫一個(gè)代碼塊
//TRY_LOCK
if?redis.call("EXISTS",KEYS[1])?>?0?then
????local?kus?=?redis.call("HKEYS",KEYS[1]);
????for?i?=?1,?#kus?do
????????if?redis.call('setnx',kus[i],ARGV[1])?>?0?then
????????????redis.call('expire',?kus[i],?ARGV[2])
????????????return?kus[i];
????????end
????end
else
????return?nil;
end
//DECR
local?kusName?=?KEYS[1];?local?key?=?KEYS[2];
local?uid?=??redis.call('get',key);
local?kuName?=?redis.call('HGET',kusName,key);
local?kuNum?=?redis.call('get',kuName);
if?redis.call("EXISTS",kusName)?>?0?then
????if?KEYS[3]?==?uid?then
????????if?kuNum?~=?nil?and?tonumber(kuNum)?<=?1?then
????????????return?redis.call('DECR',kuName);
????????else
????????????return?null;
????????end
????end
else
????return?-1;
end
//UN_LOCK
local?kusName?=?KEYS[1];
local?key?=?KEYS[2];
local?uid?=??redis.call('get',key);
local?kuName?=?redis.call('HGET',kusName,key);
local?kuNum?=?redis.call('get',kuName);
if?uid?==?KEYS[3]?then
????if?kuNum?~=nil?and?tonumber(kuNum)?<=?0?then
????????redis.call('hdel',kusName,key);
????????redis.call('del',kuName);
????end
????return?redis.call('del',key);
end
return?false;
package?com.suoxin.core.lock;
import?com.suoxin.utils.RedisTools;
import?org.slf4j.Logger;
import?org.slf4j.LoggerFactory;
import?javax.annotation.Resource;
import?java.util.Arrays;
import?java.util.List;
/**
?*?@author?所心
?*?@create?2020-09-01?下午?5:11
?*?@description?Redis分段鎖
?*/
public?class?RedisDistributedLock?{
????Logger?log?=?LoggerFactory.getLogger(RedisDistributedLock.class);
????@Resource
????private?RedisTools?tools;
?//這里我使用得是lua得方式
????private?static?final?String?TRY_LOCK?=?"if?redis.call('EXISTS',KEYS[1])?>?0?then?local?kus?=?redis.call('HKEYS',KEYS[1]);?for?i?=?1,?#kus?do?if?redis.call('setnx',kus[i],ARGV[1])?>?0?then?redis.call('expire',?kus[i],?ARGV[2])?return?kus[i];?end?end?else?return?nil;?end";
????private?static?final?String?DECR?=?"local?kusName?=?KEYS[1];?local?key?=?KEYS[2];?local?uid?=??redis.call('get',key);?local?kuName?=?redis.call('HGET',kusName,key);?local?kuNum?=?redis.call('get',kuName);?if?KEYS[3]?==?uid?then?if?kuNum?~=?nil?and?tonumber(kuNum)?>=?1?then?return?redis.call('DECR',kuName);?else?return?nil;?end?end";
????private?static?final?String?UN_LOCK?=?"local?kusName?=?KEYS[1];?local?key?=?KEYS[2];?local?uid?=??redis.call('get',key);?local?kuName?=?redis.call('HGET',kusName,key);?local?kuNum?=?redis.call('get',kuName);?if?uid?==?KEYS[3]?then?if?kuNum?~=nil?and?tonumber(kuNum)?<=?0?then?redis.call('hdel',kusName,key);?redis.call('del',kuName);?end?return?redis.call('del',key);?end?return?false;";
????private?String?KUS_NAME?=?null;
????private?String?UNIQUE_ID?=?null;
????private?String?EXPIRE?=?"60";
????private?String?KEY_ID?=?null;
????/**
?????*
?????*?@param?KUS_NAME?分段庫(kù)名稱
?????*?@param?UNIQUE_ID?唯一標(biāo)識(shí)
?????*?@param?EXPIRE?過(guò)期時(shí)間?以秒為單位
?????*/
????public?RedisDistributedLock(String?KUS_NAME,?String?UNIQUE_ID,?String?EXPIRE)?{
????????this.KUS_NAME?=?KUS_NAME;
????????this.UNIQUE_ID?=?UNIQUE_ID;
????????this.EXPIRE?=?EXPIRE;
????????//log.info("參數(shù)初始化:===>總倉(cāng)庫(kù)名稱:?{},?唯一標(biāo)識(shí):?{},?過(guò)期時(shí)間:?{}",KUS_NAME,UNIQUE_ID,EXPIRE);
????}
????/**
?????*??初始化工具類
?????*?@param?tools
?????*/
????public?void?setTools(RedisTools?tools)?{
????????this.tools?=?tools;
????}
????/**
?????*?獲取鎖?前面設(shè)置的時(shí)間就是鎖得過(guò)期時(shí)間
?????*?@return
?????*/
????public?boolean?tryLock(){
????????List?keys?=?Arrays.asList(KUS_NAME);
????????List?args?=?Arrays.asList(UNIQUE_ID,EXPIRE);
????????Object?result?=?tools.luaScript(TRY_LOCK,?keys,?args);
????????if?(result?!=?null?&&?(result.toString().length()?>?3)){
????????????this.KEY_ID?=?result.toString();
????????????//log.info("倉(cāng)庫(kù)ID:?{}?返回結(jié)果:?{}?獲取鎖成功",KEY_ID,result);
????????????return?true;
????????}?else?{
????????????//log.info("倉(cāng)庫(kù)ID:?{}?返回結(jié)果:?{}?獲取鎖失敗",KEY_ID,result);
????????????return?false;
????????}
????}
????/**
?????*?原子性扣減
?????*?@return
?????*/
????public?Integer?decr(){
????????Object?result?=?null;
????????if?(KEY_ID?!=?null){
????????????List?keys?=?Arrays.asList(KUS_NAME,KEY_ID,UNIQUE_ID);
????????????List?args?=?Arrays.asList();
????????????result?=?tools.luaScript(DECR,keys,args);
????????????if?(result?!=?null?&&?!result.toString().isEmpty()){
????????????????log.info("消費(fèi)成功?返回結(jié)果:?{}",result);
????????????????return?1;
????????????}else?{
????????????????//log.info("消費(fèi)失敗?返回結(jié)果:?{}",result);
????????????????return?0;
????????????}
????????}
????????log.info("已售空...{}"+?result);
????????return?-1;
????}
????/**
?????*?釋放鎖
?????*/
????public?void?unLock(){
????????if?(KEY_ID?!=?null){
????????????List?keys?=?Arrays.asList(KUS_NAME,KEY_ID,UNIQUE_ID);
????????????List?args?=?Arrays.asList();
????????????Object?result?=?tools.luaScript(UN_LOCK,?keys,?args);
????????????//log.info("釋放鎖?返回結(jié)果:?{}",result);
????????}
????}
}
工具類代碼:
package?com.suoxin.utils;
import?org.springframework.data.redis.core.RedisCallback;
import?org.springframework.data.redis.core.RedisTemplate;
import?org.springframework.util.CollectionUtils;
import?redis.clients.jedis.Jedis;
import?redis.clients.jedis.JedisCluster;
import?java.util.*;
import?java.util.concurrent.TimeUnit;
/**
?*?@author?所心
?*?@create?2020-08-06?下午?11:01
?*?@description?RedisUtils?工具類
?*/
public?class?RedisTools?{
????private?RedisTemplate?redisTemplate;
????public?void?setRedisTemplate(RedisTemplate?redisTemplate)?{
????????this.redisTemplate?=?redisTemplate;
????}
????
????/**
?????*?@param?key???鍵
?????*?@param?value?值
?????*?@return?true成功?false失敗
?????*?普通緩存放入
?????*/
????public?boolean?set(String?key,?Object?value)?{
????????try?{
????????????redisTemplate.opsForValue().set(key,?value);
????????????return?true;
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????????return?false;
????????}
????}
????
????/**
?????*?@param?key?鍵
?????*?@param?map?對(duì)應(yīng)多個(gè)鍵值
?????*?@return?true?成功?false?失敗
?????*
?????*??HashSet
?????*/
????public?boolean?hmset(String?key,?Map?map)?{
????????try?{
????????????redisTemplate.opsForHash().putAll(key,?map);
????????????return?true;
????????}?catch?(Exception?e)?{
????????????e.printStackTrace();
????????????return?false;
????????}
????}
????????????
????public?Map?clear()?{
????????Map?map?=?new?HashMap<>();
????????try?{
????????????//?獲取所有key
????????????Set?keys?=?redisTemplate.keys("*");
????????????assert?keys?!=?null;
????????????//?迭代
????????????Iterator?it1?=?keys.iterator();
????????????while?(it1.hasNext())?{
????????????????//?循環(huán)刪除
????????????????redisTemplate.delete(it1.next());
????????????}
????????????map.put("code",?1);
????????????map.put("msg",?"清理全局緩存成功");
????????????return?map;
????????}?catch?(Exception?e)?{
????????????map.put("code",?-1);
????????????map.put("msg",?"清理全局緩存失敗");
????????????return?map;
????????}
????}
????public?Object?luaScript(String?script,?List?keys,?List?args){
????????return?redisTemplate.execute((RedisCallback 六、測(cè)試Controller
package?com.suoxin.controller;
import?com.suoxin.core.lock.RedisDistributedLock;
import?com.suoxin.utils.RedisTools;
import?org.springframework.web.bind.annotation.RequestMapping;
import?org.springframework.web.bind.annotation.RequestParam;
import?org.springframework.web.bind.annotation.RestController;
import?javax.annotation.Resource;
import?java.util.HashMap;
import?java.util.Map;
import?java.util.UUID;
import?java.util.concurrent.ConcurrentHashMap;
/**
?*?@author?所心
?*?@create?2020-09-03?下午?1:22
?*?@description?測(cè)試Controller
?*/
@RestController
public?class?SeckillController?{
????@Resource
????private?RedisTools?tools;
????private?RedisDistributedLock?lock?=?null;
?/**
?????*
?????*?@param?kuname?Redis初始化得Redis庫(kù)名稱
?????*?@param?allnum?庫(kù)存總數(shù)
?????*?@param?subnum?分段數(shù)
?????*?@return?僅是測(cè)試沒(méi)有寫太完美但是核心代碼還是五臟俱全得
?????*/
????@RequestMapping("/init")
????public?Map?init(@RequestParam("kuname")?String?kuname,@RequestParam("allnum")?Integer?allnum,@RequestParam("subnum")?Integer?subnum){
????????ConcurrentHashMap?map?=?new?ConcurrentHashMap();
????????int?max=100000,min=1000;
????????for?(int?i?=?0;?i?(allnum/subnum);?i++)?{
????????????long?nowtime?=?System.currentTimeMillis();
????????????int?ran2?=?(int)?(Math.random()*(max-min)+min);
????????????map.put(UUID.randomUUID().toString(),?Long.parseLong(nowtime+""+ran2));
????????????tools.set(nowtime+""+ran2,?subnum);
????????}
????????tools.hmset(kuname,?map);
????????Map?map1?=?new?HashMap<>();
????????map1.put("msg","成功");
????????return?map1;
????}
?/**
?????*
?????*?@param?kuname?初始化時(shí)得Redis倉(cāng)庫(kù)名稱
?????*?@param?timeout?超時(shí)時(shí)間
?????*?@return
?????*/
????@RequestMapping("/seckill")
????public?Map?seckill(@RequestParam("kuname")?String?kuname,@RequestParam(value?=?"timeout",defaultValue?=?"5")?String?timeout){
????????Map?map1?=?new?HashMap<>();
????????long?run?=?System.currentTimeMillis();
????????//初始化鎖
????????lock?=?new?RedisDistributedLock(kuname,?UUID.randomUUID().toString(),?timeout);
????????lock.setTools(tools);
????????try?{
????????????//獲取鎖
????????????if?(lock.tryLock())?{
????????????????map1.put("msg","恭喜您搶到了!!!:"?+?lock.decr());
????????????}?else?{
????????????????map1.put("msg","很遺憾您沒(méi)有搶到?╥﹏╥...");
????????????}
????????}?finally?{
????????????//計(jì)算時(shí)間
????????????long?end??=?System.currentTimeMillis();
????????????map1.put("elapsedTime","耗時(shí):===>"+?(end-run)?+"ms");
????????????//釋放鎖
????????????lock.unLock();
????????????return?map1;
????????}
????}
}
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循?CC 4.0 BY-SA?版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接和本聲明。
本文鏈接:
http://blog.csdn.net/G3187576034/article/details/108482317
粉絲福利:108本java從入門到大神精選電子書領(lǐng)取
???
?長(zhǎng)按上方鋒哥微信二維碼?2 秒 備注「1234」即可獲取資料以及 可以進(jìn)入java1234官方微信群
感謝點(diǎn)贊支持下哈?
