redis同时设置key和过期时间实现分布式锁

我们想通过同时设置redis的一个key值,用来表示占用到锁。可以用setnx命令实现。但如果此时redis服务发生异常,锁永远无法释放则会产生异常。为了避免这个现象,可以给key设置过期时间,但是两个命令分开执行时,两次命令中间的这段时间也可能产生异常。所以需要设置key-value和expire作为一个原子操作。redis的set命令本身支持这种操作。

SET key value [EX seconds|PX milliseconds|EXAT timestamp|PXAT
milliseconds-timestamp|KEEPTTL] [NX|XX] [GET]

  • EX seconds – Set the specified expire time, in seconds.
  • PX milliseconds – Set the specified expire time, in milliseconds.
  • EXAT timestamp-seconds – Set the specified Unix time at which the key will expire, in seconds.
  • PXAT timestamp-milliseconds – Set the specified Unix time at which the key will expire, in milliseconds.
  • NX – Only set the key if it does not already exist.
  • XX – Only set the key if it already exist.
  • KEEPTTL – Retain the time to live associated with the key.
  • GET – Return the old value stored at key, or nil when key did not exist.

参考链接redis官方文档

于是将这个操作通过如下方式封装,留出key的值用来表示锁,过期时间表示锁过期时间即可。这中方式实现的分布式锁相比于redisson实现的分布式锁,可以更细粒度的控制每个具体锁的过期时间,缺点是没有自动续锁。

package com.dahua.psi.cache.redis.helper;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;

/**
 * redis锁帮助类,与redisson的分布式锁相比,可任意修改锁超时时间
 *
 * @author susq
 * @date 2020年11月11日
 */
@Component
public class RedisClusterHelper implements InitializingBean {

   public boolean setExNx(String key, long expire) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            byte[] exp = String.valueOf(expire).getBytes(Charset.forName("UTF-8"));
            Object obj = connection.execute("set", keySerializer.serialize(key), valueSerializer.serialize("1"), nx, ex, exp);
            return obj != null;
        });
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        keySerializer = redisTemplate.getKeySerializer();
        valueSerializer = redisTemplate.getValueSerializer();
    }

	@Autowired
    private RedisTemplate<String, String> redisTemplate;

    RedisSerializer keySerializer;

    RedisSerializer valueSerializer;

    private byte[] nx = "NX".getBytes(Charset.forName("UTF-8"));
    private byte[] ex = "EX".getBytes(Charset.forName("UTF-8"));
    
    /**
     * 获取一个redis锁
     * @param key 锁的key
     * @param expire 过期时间,单位秒
     * @return 是否获取成功
     */
    public boolean setExNx(String key, long expire) {
        return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
            byte[] exp = String.valueOf(expire).getBytes(Charset.forName("UTF-8"));
            Object obj = connection.execute("set", keySerializer.serialize(key), valueSerializer.serialize("1"), nx, ex, exp);
            return obj != null;
        });
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        keySerializer = redisTemplate.getKeySerializer();
        valueSerializer = redisTemplate.getValueSerializer();
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值