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

目录

应用场景

代码实例1:

代码实例2:

setIfAbsent:

expire:

举例说明:

代码实例3:

代码实例4:


修改时间:2024年6月11日09:37:39

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

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

应用场景

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

代码实例1:

redisTemplate.opsForValue().setIfAbsent("ruleRefreshLock", 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){
                //获取到锁,那么设置过期时间,防止死锁
                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 用于设置锁的过期时间以确保锁最终会被释放。两种方式的用法看对应的应用场景来使用

  • 25
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
对于Redis自定义分布式,你可以使用注解来简化的使用。下面是一个示例: 首先,你可以创建一个自定义的注解,例如`@DistributedLock`。这个注解可以用来修饰需要加的方法。 ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DistributedLock { String value() default ""; // 的key,默认为空 long expire() default 30000; // 的过期时间,默认为30秒 } ``` 接下来,你可以创建一个切面类,用于处理`@DistributedLock`注解。在该切面类,使用Redis来实现分布式的功能。 ```java @Aspect @Component public class DistributedLockAspect { private final RedisTemplate<String, String> redisTemplate; public DistributedLockAspect(RedisTemplate<String, String> redisTemplate) { this.redisTemplate = redisTemplate; } @Around("@annotation(distributedLock)") public Object distributedLock(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable { String lockKey = distributedLock.value(); // 获取的key long expire = distributedLock.expire(); // 获取的过期时间 // 加操作 Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", expire, TimeUnit.MILLISECONDS); if (locked != null && locked) { try { // 执行方法体 return joinPoint.proceed(); } finally { // 释放 redisTemplate.delete(lockKey); } } else { throw new RuntimeException("Failed to acquire distributed lock."); } } } ``` 在上述代码,我们使用`@Around`注解将切面绑定到所有使用`@DistributedLock`注解的方法上。在切面方法内部,首先获取的key和过期时间,然后尝试加。如果成功获取到,则执行方法体,并在方法执行完毕后释放。如果获取失败,则抛出异常。 最后,你可以在需要加的方法上使用`@DistributedLock`注解。 ```java @Service public class ExampleService { @Autowired private RedisTemplate<String, String> redisTemplate; @DistributedLock(value = "myLockKey", expire = 5000) public void exampleMethod() { // 需要加的方法体 } } ``` 上述示例,`exampleMethod()`方法使用了`@DistributedLock`注解,并指定了的key为"myLockKey",过期时间为5秒。 这样,你就可以通过注解方式来简化Redis自定义分布式的使用了。请注意,在实际使用,你需要配置好Redis连接和注入RedisTemplate实例。
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一百减一是零

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值