高併發情況下分佈式鎖 (setnx)鎖不住的問題(多實例/主從)解決

最近遇到一個比較奇怪的問題,在秒殺的時候,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 裏面進行操作,無論如何都會進行鎖的釋放。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章