redis中的分布式锁(setIfAbsent)(expire)

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

还是一个同事问的一个问题,然后闲着没事就记录下来了。多人操作同一个保单,会出现数据不一致,所以呢,就准备为此单子加一个锁,所以就有了下面的代码。分享给大家

分布式锁的定义大家基本都很了解了,具体就是当需要在分布式环境中保证资源的互斥访问或一致性时,就可以考虑使用分布式锁。但需要注意的是,分布式锁虽然可以解决问题,但也会带来一定的性能开销和复杂度,因此在使用时需要权衡系统的性能和可维护性,以及使用分布式锁所带来的成本和风险。

应用场景

  1. 发任务调度:在分布式系统中,当需要对某个任务进行调度时,如果多个节点同时发起任务调度请求,可能会导致任务被多次执行。这时可以使用分布式锁来确保只有一个节点能够成功获取到任务调度的锁,从而避免任务的重复执行。
  2. 分布式事务:在分布式事务中,需要保证不同节点对共享资源的访问是互斥的,以防止数据不一致的问题。分布式锁可以用来保证在事务执行过程中,各个节点之间对共享资源的访问是互斥的。
  3. 分布式缓存:在分布式缓存中,如果多个节点同时对缓存进行访问和修改,可能会导致缓存中的数据不一致。这时可以使用分布式锁来确保各个节点对缓存的访问是互斥的。
  4. 购买限制:在电商等场景中,如果需要对某个商品的购买数量进行限制,当多个用户同时发起购买请求时,可以使用分布式锁来确保只有一个用户能够成功获取到购买锁,从而避免超卖等问题。

代码实例1:

redisTemplate.opsForValue().setIfAbsent("redisLock", uuid, 1000, TimeUnit.SECONDS);

这段代码是使用Spring Data Redis的RedisTemplate来执行一个Redis操作。具体来说,它尝试在Redis中设置一个键值对,但仅当该键不存在时。

详细解释如下:

  • "ruleRefreshLock":这是要设置的Redis键。
  • uuid:这是要设置的Redis值。
  • 1000:这是值的过期时间,单位是毫秒。
  • TimeUnit.SECONDS:这是过期时间的单位,这里是秒。

setIfAbsent方法的行为如下:

  • 如果键"ruleRefreshLock"在Redis中不存在,那么它会设置这个键的值为uuid,并给这个键设置一个1000秒的过期时间。此时,setIfAbsent方法返回true
  • 如果键"ruleRefreshLock"在Redis中已经存在,那么它不会做任何操作,并且setIfAbsent方法返回false

这个方法通常用于实现分布式锁。在这个例子中,你可能想要确保只有一个实例或线程能够获取到"ruleRefreshLock"这个锁。如果锁已经被其他实例或线程获取(即键已经存在),那么当前实例或线程就不会再尝试获取锁。如果锁未被获取(即键不存在),那么当前实例或线程就会获取锁,并设置一个过期时间以确保锁最终会被释放。

代码实例2:

设置KEY值过期时间

//假设 lockName 是你的锁的名称,expireTime 是你希望锁持续的时间(以分钟为单位)
redisTemplate.expire(lockName, expireTime, TimeUnit.MINUTES);

这段代码是使用 RedisTemplate 来设置 Redis 中某个键的过期时间。这与之前的 setIfAbsent 操作有所不同,主要区别如下:

setIfAbsent

  • 目的:尝试设置一个键值对,但仅当该键不存在时。
  • 返回值:如果键原本不存在且成功设置了键值对,返回 true;如果键已经存在,则不做任何操作并返回 false
  • 用途:通常用于实现分布式锁或其他需要原子性设置键值对的场景。

expire

  • 目的:为已存在的键设置或更新其过期时间。
  • 返回值:如果键存在,则成功设置或更新过期时间并返回 true;如果键不存在,则不进行任何操作并返回 false
  • 用途:通常用于确保存储在 Redis 中的数据在一段时间后被自动删除,以避免内存无限制增长。

举例说明:

假设你已经有了一个名为 lockName 的锁,并且这个锁已经被某个实例获取(即该键在 Redis 中已经存在)。你想让这个锁在一段时间后自动释放,那么你可以使用 expire 方法来设置锁的过期时间。

代码实例3:

redis版本        spring-data-redis-2.7.10.jar

@Slf4j
@Component
public class redisSchedule implements SchedulingConfigurer  {
 
    @Value("${rule.limit.cron}")
    private String cron;
    @Value("${schedule.rule}")
    private String scheduleRule;
    @Autowired
    private StringRedisTemplate redisTemplate;
 
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addCronTask(() -> {
                // 分布式锁,集群环境只允许一台服务修改定时任务
                String uuid = UUID.randomUUID().toString();
                Boolean bool = redisTemplate.opsForValue().setIfAbsent("redisLock", uuid, 1000, TimeUnit.SECONDS);
                try {
                    if (bool) {
                        //逻辑代码
                    }
                } catch (Exception e) {
                    log.error(e.getMessage());
                } finally {
                    //  释放锁,先比对自己锁的值是否相等,相等则为自己的锁
                    String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                    redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("redisLock"), uuid);
                }
        }, cron);
    }
}

代码实例4:

redis版本        spring-data-redis-1.8.0.RELEASE.jar

//锁的过期时间,默认为5分钟
private Long expireTime = 5L;
 
try {
            //多个执行任务去获得锁,如果没有获取证明还有任务在执行中,那么将不再执行任务
            Boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockName, "lock");
            if (isLock){
                //获取到锁,那么设置过期时间,防止死锁
                this.redisTemplate.expire(lockName, expireTime, TimeUnit.MINUTES);
                //逻辑代码
            }else {
                logger.info("任务执行锁定失败,Lock被占用,当前分片:=====");
                throw new Exception("任务执行锁定失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(e.getMessage());
        }

总结一下,setIfAbsent 用于在键不存在时设置键值对,并返回操作是否成功的布尔值;而 expire 用于设置或更新已存在键的过期时间,并返回操作是否成功的布尔值。这两个操作经常一起使用来实现分布式锁,其中 setIfAbsent 用于尝试获取锁,而 expire 用于设置锁的过期时间以确保锁最终会被释放。两种方式的用法看对应的应用场景来使用


相关实践学习
基于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
相关文章
|
26天前
|
存储 缓存 NoSQL
Redis常见面试题(二):redis分布式锁、redisson、主从一致性、Redlock红锁;Redis集群、主从复制,哨兵模式,分片集群;Redis为什么这么快,I/O多路复用模型
redis分布式锁、redisson、可重入、主从一致性、WatchDog、Redlock红锁、zookeeper;Redis集群、主从复制,全量同步、增量同步;哨兵,分片集群,Redis为什么这么快,I/O多路复用模型——用户空间和内核空间、阻塞IO、非阻塞IO、IO多路复用,Redis网络模型
Redis常见面试题(二):redis分布式锁、redisson、主从一致性、Redlock红锁;Redis集群、主从复制,哨兵模式,分片集群;Redis为什么这么快,I/O多路复用模型
|
29天前
|
NoSQL Java Redis
分布式锁实现原理问题之使用Redis的setNx命令来实现分布式锁问题如何解决
分布式锁实现原理问题之使用Redis的setNx命令来实现分布式锁问题如何解决
|
18天前
|
缓存 NoSQL 关系型数据库
(八)漫谈分布式之缓存篇:唠唠老生常谈的MySQL与Redis数据一致性问题!
本文来聊一个跟实际工作挂钩的老生常谈的问题:分布式系统中的缓存一致性。
69 10
|
20天前
|
NoSQL 算法 Java
(十三)全面理解并发编程之分布式架构下Redis、ZK分布式锁的前世今生
本文探讨了从单体架构下的锁机制到分布式架构下的线程安全问题,并详细分析了分布式锁的实现原理和过程。
|
4天前
|
NoSQL Java Redis
Redis字符串数据类型之INCR命令,通常用于统计网站访问量,文章访问量,实现分布式锁
这篇文章详细解释了Redis的INCR命令,它用于将键的值增加1,通常用于统计网站访问量、文章访问量,以及实现分布式锁,同时提供了Java代码示例和分布式锁的实现思路。
11 0
|
29天前
|
存储 缓存 NoSQL
高并发架构设计三大利器:缓存、限流和降级问题之Redis用于搭建分布式缓存集群问题如何解决
高并发架构设计三大利器:缓存、限流和降级问题之Redis用于搭建分布式缓存集群问题如何解决
|
20天前
|
数据采集 存储 NoSQL
Redis 与 Scrapy:无缝集成的分布式爬虫技术
Redis 与 Scrapy:无缝集成的分布式爬虫技术
|
26天前
|
NoSQL 前端开发 算法
Redis问题之Redis分布式锁与Zookeeper分布式锁有何不同
Redis问题之Redis分布式锁与Zookeeper分布式锁有何不同
|
NoSQL Java 关系型数据库
浅谈Redis实现分布式锁
浅谈Redis实现分布式锁
|
存储 canal 缓存