redis實現分布式鎖


清理郵件的時候發現之前看的一篇關於redis分布式鎖實現的文章有人回復- -當時隨意掃了眼文章,為了防止發生死鎖,思路是使用setnx設置value為本地時間,然后獲取鎖失敗時讀取value進行時間比對。。然后我回復了下。。多台應用服務器存在時間不同步的問題。。

其實使用setnx時設置下redis過期時間簡單方便,只是通常在應用程序內通過sdk做這項操作時由於賦值+設置過期不在同一原子性操作中。。所以很多人覺得不可行了。。那就直接使用lua腳本唄,簡單方便,原子性操作,性能也OK。

加鎖:aquire_lock_with_timeout.lua

if redis.call("exists",KEYS[1]) == 0 then
    local lockSrc = redis.call("setex",KEYS[1],unpack(ARGV))
    if lockSrc then
        return "1"
    end
        return "0"
end
return "-1"

釋放鎖:release_lock.lua

if redis.call("get",KEYS[1]) == ARGV[1] then
    local lockRelease = redis.call("del",KEYS[1])
        if lockRelease then
            return "1"
        end
            return "0"
end
return "-1"

java為例,加載兩個lua腳本,然后簡單加鎖,釋放鎖

  @Bean
    @Qualifier("lockScript")
    public RedisScript<Integer> acquireLockWithTimeout() {
        DefaultRedisScript redisScript = new DefaultRedisScript();
        redisScript.setLocation(new ClassPathResource("redis/acquire_lock_with_timeout.lua"));
        redisScript.setResultType(Integer.class);
        return redisScript;
    }


    @Bean
    @Qualifier("unLockScript")
    public RedisScript<Integer> releaseLock() {
        DefaultRedisScript redisScript = new DefaultRedisScript();
        redisScript.setLocation(new ClassPathResource("redis/release_lock.lua"));
        redisScript.setResultType(Integer.class);
        return redisScript;
    }
    private Integer acquireTimeout;//資源占有鎖的時間 秒s
    private Integer acquireInterval;//嘗試獲取鎖的時限 ms
    private Integer lockTime;//嘗試獲取鎖的時間間隔 ms
    
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    @Qualifier("lockScript")
    private RedisScript<Integer> acquireLockWithTimeout;
    @Autowired
    @Qualifier("unLockScript")
    private RedisScript<Integer> releaseLock;

     
    public String tryLock(String lockKey) {
        String lockValue = UUID.randomUUID().toString();
        Long endTime = System.currentTimeMillis() + acquireTimeout;
        while (System.currentTimeMillis() < endTime) {
            Integer lockResult = (Integer) redisTemplate.execute(acquireLockWithTimeout, Collections.singletonList(lockKey), lockTime, lockValue);
            if (lockResult.equals(1)) {
                return lockValue;
            } else {
                try {
                    Thread.sleep(acquireInterval);
                } catch (InterruptedException ex) {
                    continue;
                }
            }
        }
        return "";
    }
 
    public boolean releaseLock(String lockKey, String lockValue) {
        Integer releaseResult = (Integer) redisTemplate.execute(releaseLock, Collections.singletonList(lockKey), lockValue);
        if (releaseResult.equals(1)) {
            return true;
        }
        return false;
    }

當然java也有更復雜更豐富的組件 redisson

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM