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

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Redis 版,倚天版 1GB 1个月
简介: redis 分布式锁

基于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("等待获取锁超时");
}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
18天前
|
缓存 NoSQL 关系型数据库
(八)漫谈分布式之缓存篇:唠唠老生常谈的MySQL与Redis数据一致性问题!
本文来聊一个跟实际工作挂钩的老生常谈的问题:分布式系统中的缓存一致性。
72 10
|
22天前
|
存储 缓存 NoSQL
Redis 全局通用命令整理
Redis 全局通用命令整理
48 10
|
20天前
|
NoSQL 算法 Java
(十三)全面理解并发编程之分布式架构下Redis、ZK分布式锁的前世今生
本文探讨了从单体架构下的锁机制到分布式架构下的线程安全问题,并详细分析了分布式锁的实现原理和过程。
|
22天前
|
存储 JSON NoSQL
Redis常见命令大全
【7月更文挑战第30天】
11 3
|
22天前
|
NoSQL Redis 索引
Redis 中ZSET数据类型命令使用及对应场景总结
Redis 中ZSET数据类型命令使用及对应场景总结
31 2
|
22天前
|
NoSQL Redis
Redis set数据类型命令使用及应用场景使用总结
Redis set数据类型命令使用及应用场景使用总结
22 1
|
4天前
|
NoSQL Java Redis
Redis字符串数据类型之INCR命令,通常用于统计网站访问量,文章访问量,实现分布式锁
这篇文章详细解释了Redis的INCR命令,它用于将键的值增加1,通常用于统计网站访问量、文章访问量,以及实现分布式锁,同时提供了Java代码示例和分布式锁的实现思路。
12 0
|
20天前
|
数据采集 存储 NoSQL
Redis 与 Scrapy:无缝集成的分布式爬虫技术
Redis 与 Scrapy:无缝集成的分布式爬虫技术
|
NoSQL 测试技术 Redis
Redis命令性能优化及事务使用过程(下)
Redis命令性能优化及事务使用过程(下)
523 0
Redis命令性能优化及事务使用过程(下)
|
存储 移动开发 NoSQL
Redis命令性能优化及事务使用过程(上)
Redis命令性能优化及事务使用过程(上)
207 0
Redis命令性能优化及事务使用过程(上)