【redis】——分布式锁


原创hy_coming 最后发布于2019-07-07 16:56:10 阅读数 33  已收藏
展开
实例代码地址,请前往:https://gitee.com/GuoqingLee/distributed-seckill

redis官方文档地址,请前往:http://www.redis.cn/topics/distlock.html

前言
关于分布式锁的实现,目前主流方案有以下三类:

1、基于数据库的乐观锁;

2、基于redis实现的锁服务;

3、基于zookeeper的实现;

本文注释基于redis实现分布式锁,一种是最普通的实现方式,另外一种是redis官网推荐的实现方式redission实现,接下看看实现代码

一、普通实现方式
普通实现方式是基于以下两个命令

#使用redis中的setnx命令创建一个key,成功创建就是加锁
SET resource_name my_random_value NX PX 30000
 
#删除锁的时候,找到 key 对应的 value,跟自己传过去的 value 做比较,如果是一样的才删除。
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end
依赖引入

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
代码实现

/**
 * @author  2019/7/7 15:49
 */
public class RedisLocker {
    @Autowired
    private JedisPool jedisPool;
 
    /**
     * 获取锁
     *
     * @param key
     * @param value
     * @param leaseTime 过期时间
     * @return
     */
    public boolean tryLock(String key, String value, long leaseTime) {
        try (Jedis jedis = jedisPool.getResource()) {
            if (jedis == null) {
                return false;
            }
            String result = jedis.set(key, value, "NX", "PX", leaseTime);
            return "OK".equalsIgnoreCase(result);
        } catch (Exception e) {
            return false;
        }
    }
 
    /**
     * 释放锁 
     * @param key
     * @param value
     * @return
     */
    public int unlock(String key, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            if (jedis == null) {
                return 0;
            }
            StringBuilder builder = new StringBuilder();
            builder.append("if redis.call('get','")
                    .append(key)
                    .append("')")
                    .append("=='")
                    .append(" then ")
                    .append("  return redis.call('del','")
                    .append(key)
                    .append("')")
                    .append(" else ")
                    .append(" return 0")
                    .append(" end");
            return Integer.valueOf(jedis.eval(builder.toString()).toString());
        }catch (Exception e){
            return 0;
        }
    }
 
 
}
结果请自行测试

二、基于redission实现
依赖引入

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.10.1</version>
        </dependency>
配置文件

#redis config
spring.redis.database=
spring.redis.host=
spring.redis.port=
spring.redis.password=
spring.redis.ssl=
spring.redis.timeout=
spring.redis.cluster.nodes=
spring.redis.sentinel.master=
spring.redis.sentinel.nodes=
代码实现

/**
 * 基于redission的分布式实现
 *
 * @author  2019/7/7 15:20
 */
@Component
public class RedissonDistributedLocker {
    @Autowired
    private RedissonClient redissonClient;
 
    /**
     * 加锁
     *
     * @param lockKey
     * @return
     */
    public RLock lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        return lock;
    }
 
 
    /**
     * 加锁,过期自动释放
     *
     * @param lockKey
     * @param leaseTime
     * @return
     */
    public RLock lock(String lockKey, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, TimeUnit.SECONDS);
        return lock;
    }
 
    /**
     * 加锁,过期自动释放,传入时间单位
     *
     * @param lockKey
     * @param leaseTime
     * @param unit
     * @return
     */
    public RLock lock(String lockKey, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, unit);
        return lock;
    }
 
    /**
     * 尝试获取锁,传入时间单位
     *
     * @param lockKey
     * @param unit
     * @param waitTime
     * @param leaseTime
     * @return
     */
    public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            return false;
        }
    }
 
    /**
     * 尝试获取锁
     *
     * @param lockKey
     * @param waitTime
     * @param leaseTime
     * @return
     */
    public boolean tryLock(String lockKey, long waitTime, long leaseTime) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            return false;
        }
    }
 
    /**
     * 释放锁,传入key
     *
     * @param lockKey
     */
    public void unlock(String lockKey) {
        redissonClient.getLock(lockKey).unlock();
    }
 
    /**
     * 释放锁,传入lock
     *
     * @param lock
     */
    public void unlock(RLock lock) {
        lock.unlock();
    }
}
结果测试

#方式一
@Autowired
 private RedissonDistributedLocker redissonDistributedLocker;
 
String lockKey = "BM_MARKET_SECKILL_" + stallActivityId;
try {
                //超过2S自动释放锁
        redissonDistributedLocker.lock(lockKey, 2L);
                //业务处理
 
    } finally {
        redissonDistributedLocker.unlock(lockKey);  //释放锁
    }
 
#方式二
@Autowired
private RedissonDistributedLocker redissonDistributedLocker;
 
public void test() throws InterruptedException {
    final int[] counter = {0};
 
        for (int i= 0; i < 100; i++){
        
            new Thread(new Runnable() {
 
                @Override
 
                public void run() {
                    boolean isGetLock = redissonDistributedLocker.tryLock("test0001", 3L, 1L);
                    if(isGetLock) {
                        try {
                            int a = counter[0];
                            counter[0] = a + 1;
                            logger.info(a + "");
                        } finally {
                            redissonDistributedLocker.unlock("test0001");
                        }
                    }
                }
            }).start();
            
        }
 
        // 主线程休眠,等待结果
        Thread.sleep(5000);
        System.out.println(counter[0]);
        logger.info(counter[0] + "");
}
三、redis 分布式锁和 zk 分布式锁的对比
redis 分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能。
zk 分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小。
另外一点就是,如果是 redis 获取锁的那个客户端 出现 bug 挂了,那么只能等待超时时间之后才能释放锁;而 zk 的话,因为创建的是临时 znode,只要客户端挂了,znode 就没了,此时就自动释放锁。

redis 分布式锁大家没发现好麻烦吗?遍历上锁,计算时间等等......zk 的分布式锁语义清晰实现简单。

参考:https://www.cnblogs.com/ocean-sky/p/10320627.html

http://www.redis.cn/topics/distlock.html

发布了357 篇原创文章 · 获赞 228 · 访问量 179万+

猜你喜欢

转载自blog.csdn.net/wangshuminjava/article/details/105061228