实践基于redis的分布式锁实现

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/fxbin123/article/details/84227927

一、应用场景

当多个机器(多个进程)会对同一条数据进行修改时,并且要求这个修改是原子性的。这里有两个限定:(1)多个进程之间的竞争,意味着JDK自带的锁失效;(2)原子性修改,意味着数据是有状态的,修改前后有依赖。

二、实现方式

分布式锁一般有三种实现方式:1. 数据库乐观锁;基于version字段实现,乐观锁,两个线程可以同时读取到原有的version值,但是最终只有一个可以完成操作;
2. 基于Redis的NX EX参数的分布式锁
3. 基于ZooKeeper的分布式锁。

本篇博客将介绍第二种方式,基于Redis实现分布式锁。

三、代码实现

1、pom.xml 引入如下依赖

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.0.6.RELEASE</version>
        </dependency>

2、加锁

    private static final String LOCK_SUCCESS = "OK";

    /**
     * setIfAbsent 函数, , 尝试设置lockKey的值为lockValue,
     *  若成功则同时设置key的过期时间并返回true,否则返回false
     * @author fxbin
     * @param lockKey 锁
     * @param lockValue 锁标识
     * @param expireTime 过期时间,单位 ms
     * @return 是否获取成功
     */
    public boolean tryGetDistributedLock(String lockKey, String lockValue, Integer expireTime){
    
        Assert.isTrue(StringUtils.isNotBlank(lockKey), "lockKey must not be null");
        Assert.isTrue(StringUtils.isNotBlank(lockValue), "lockValue must not be null");        
        Assert.isTrue(expireTime > 0, "expireTime must greater than 0");

        log.info("尝试获取锁, 锁Key:{}, 锁标识:{}", lockKey, lockValue);
      
        RedisScript<String> script = new DefaultRedisScript<>(
            "return redis.call('set',KEYS[1],ARGV[1],'NX','PX',ARGV[2])", String.class);
        
        String locked = (String) redisTemplate.execute(script,
                redisTemplate.getStringSerializer(),
                redisTemplate.getStringSerializer(),
                Collections.singletonList(lockKey),
                lockValue, String.valueOf(expireTime));
                
        return LOCK_SUCCESS.equalsIgnoreCase(locked);
    }

3、解锁

    /**
     * 释放分布式锁
     * @author fxbin
     * @param lockKey 锁
     * @param lockValue 锁标识
     * @return 是否释放成功
     */
    public boolean releaseDistributedLock(String lockKey, String lockValue){

        log.info("尝试释放锁, 锁Key:{}, 锁标识:{}", lockKey, lockValue);

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end ";

        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Long.class);

        Long evalResult = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), lockValue);

        log.info("lua执行结果: {}", evalResult);
        
        if (RELEASE_SUCCESS.equals(evalResult)) {
            return true;
        }
        return false;
    }

– end 感谢阅读,如有问题,请留言或发邮件

猜你喜欢

转载自blog.csdn.net/fxbin123/article/details/84227927