一种redis命令搞定基于redis的分布式锁

本文介绍了一种基于Redis的分布式锁的实现方式,旨在解决高并发环境下数据处理的互斥问题,防止脏数据产生。文中详细解释了使用随机字符串作为锁值的原因及如何利用Lua脚本来确保锁的安全释放。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于redis的分布式锁项目肯定经常用到,主要是为了避免重复处理,或者由于并发带来的脏数据或者错误的处理。

使用锁就必须注意一下几点:
1、互斥,同一时间不能有多个client能获取到锁
2、不能发生死锁,不能因为有锁的client因为崩溃或者解锁命令请求失败导致无法释放锁
3、自己只能解自己上的锁,不能删除别人的锁

下面介绍一下代码为什么这么写
1、为什么不用setnx和expire两个命令来实现加锁,因为如果再执行setnx后加过期时间崩溃了,就无法解锁。
2、为什么需要使用随机字符串来做lockvalue,主要是为了防止不同client的锁都是一样的,防止client误删
3、为什么还需要Lua脚本代码来实现删除锁,纵然我们在删除之前比对了锁,但是get和del之间不是原子性的,所以也防止删除别人的锁,用lua脚本代码实现原子操作

下面是一个不侵占业务的通过redis分布式锁来执行的方法代码,当然这里只是参考,一种解决问题的方案,至于业务实现怎么写,大家可以随便发挥。

/**
 * 分布式锁
 *
 * @param r             继承于Redis
 * @param lockKey       分布式锁的key
 * @param voidMethod    获得到锁后需要执行的方法
 * @param expireSeconds 锁定时间 单位/s(需要评估方法执行时间)
 * @param retrySeconds  重试间隔 单位/s
 * @throws ServiceException
 */
public <R extends Redis> void lock(R r, String lockKey, VoidMethodInterface voidMethod, int expireSeconds, int retrySeconds) throws ServiceException {
    long begin = 0L;
    long retryMills = retrySeconds * 1000;
    //获取随机字符串,避免lockValue相同
    String lockValue = UUID.randomUUID().toString();
    while (begin <= retryMills) {
        String response = r.set(lockKey, lockValue, "nx", "ex", expireSeconds);
        if (response != null) {
            logger.debug("lock method exec begin");
            try {
                voidMethod.exec();
            } catch (Exception e) {
                logger.error("method exec failed ,msg:%s", e.getMessage());
            } finally {
                String value = r.get(lockKey);
                //当缓存里还有该key对应的值时,才去删除锁,避免执行时间过长导致锁被释放
                if (!StringUtils.isEmpty(value) && lockValue.equals(value)) {
                    // 避免若在此时,这把锁突然不是这个客户端的,则会误解锁
                    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                    r.eval(script, Collections.singletonList(lockKey), Collections.singletonList(lockValue));
                }
            }
            logger.debug("lock method exec success");
            return;
        } else {
            long waitMills = random.nextInt(WAIT_INTERVAL_MIN_MILLS, WAIT_INTERVAL_MAX_MILLS);
            try {
                Thread.sleep(waitMills);
            } catch (InterruptedException ex) {
                throw new UnexpectedStateException(ex);
            }
            begin = begin + waitMills;
            logger.debug("等待获取锁,当前等待时间:%sms", begin);
        }
    }
    logger.error("等待获取锁超时");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值