最近遇到一個比較奇怪的問題,在秒殺的時候,redis的分佈式鎖竟然沒有鎖住,在併發的時候,事務開啓的太長,沒有拿到相應的數據,從而進行的髒讀,以及髒寫。我在 爲了解決這個問題,運用lua 腳本去操作鎖
好了,現在開始 上代碼:
1.設置 redis 鎖
Boolean setResult =false;
//redis 鎖
String key = "lock:exchange"+userId+"commodity"+shopCommodityId;
//uuId 作爲 請求id
String requestId = UUID.randomUUID().toString() ;
//進行鎖 操作
setResult = distributedLock.lock(key, requestId,60000);
log.info("用戶:"+userId+"--鎖--返回結果:"+setResult);
2.redis 鎖工具類
public interface DistributedLock {
/**
* 獲取鎖
* @param requestId 請求id uuid
* @param key 鎖key
* @param expiresTime 失效時間 ms
* @return 獲取鎖成功 true
*/
boolean lock(String key,String requestId,int expiresTime);
/**
* 釋放鎖
* @param key 鎖key
* @param requestId 請求id uuid
* @return 成功釋放 true
*/
boolean releaseLock(String key,String requestId);
}
public class DistributedLockImpl implements DistributedLock{
public DistributedLockImpl(StringRedisTemplate redisTemplate){
this.redisTemplate = redisTemplate;
}
private StringRedisTemplate redisTemplate;
//lua 腳本編寫
private static String lockScript = "if redis.call('setnx',KEYS[1],ARGV[1]) == 1 then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end";
private static String releaseLockScript = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
@Override
public boolean lock( String key,String requestId, int expiresTime) {
DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(lockScript, Long.class);
Long result = redisTemplate.execute(longDefaultRedisScript, Collections.singletonList(key), requestId,String.valueOf(expiresTime));
return result == 1;
}
@Override
public boolean releaseLock(String key, String requestId) {
DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(releaseLockScript, Long.class);
Long result = redisTemplate.execute(longDefaultRedisScript, Collections.singletonList(key), requestId);
return result == 1;
}
}
3.根據返回鎖的結果 進行操作;
4.最後進行鎖的釋放,我建議在 finally 裏面進行操作,無論如何都會進行鎖的釋放。