首页 > 数据库 >Redis分布式锁

Redis分布式锁

时间:2023-07-02 23:11:07浏览次数:56  
标签:task Redis setIfAbsent error lockKey redisTemplate 分布式

一、简介

单机器环境下,可以通过锁来解决共享资源的竞争问题;而在分布式集群环境下,机器与机器之间的资源竞争则需要依赖Redis、ZooKeeper等中间件去协调。

简单总结一下自己对Redis分布式锁的一些理解

二、代码实现

第一步先是获取锁,通过setnx操作,设置指定key及其过期时间。较新的版本支持setnx和过期时间的原子性操作,如果是较老的版本,只能通过Lua脚本来完成这一步。

若setnx返回true,则代表成功获取到锁,否则没有获取到锁。

boolean setIfAbsent = false;
try {
		setIfAbsent = redisTemplate.opsForValue().setIfAbsent(lockKey, clientId, lockTimeout, TimeUnit.MILLISECONDS);
} catch (Exception e) {
		log.error("redis lock occur error", e);
}
if (!setIfAbsent) {
		return;
}

获取到锁以后开始执行task任务

执行完成后需要释放锁

try {
		redisTemplate.delete(lockKey);
} catch (Exception e) {
		logger.error("release lock occur error", e);
}

乍一看没什么问题,但是在某种情况下是有问题的,假如A获取到了锁开始执行task,但是task执行时间很长,超过了lockTimeout时间,key过期了,此时B尝试获取锁,成功获取到了,开始执行task,A完成了task执行了redisTemplate.delete操作,把B的锁给删除了,那么锁的功能就失效了。

在这种情况下就需要保证获取锁的那一方的锁不会被其他方释放,锁只能被获取方释放或过期自动释放,不能有其他被释放的情况发生。因此可以通过锁key时设置指定的value,只有相同的value才能释放锁。而这个value的比较与锁的释放必须保证原子性,需要通过Lua脚本来实现。

try {
		redisTemplate.execute(
				RedisScript.of(
						"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end",
      			Long.class
    		),
    		Collections.singletonList(lockKey),
    		clientId);
} catch (Exception e) {
  	log.error("release lock occur error", e);
}

完整的代码示例如下:

public class RedisLock {

    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * @param lockKey     锁key
     * @param clientId    锁value
     * @param lockTimeout 锁超时时间(毫秒)
     * @param task        执行类
     */
    public void lock(String lockKey, String clientId, long lockTimeout, Task task) {
        boolean setIfAbsent = false;
        try {
            setIfAbsent = redisTemplate.opsForValue().
                    setIfAbsent(lockKey, clientId, lockTimeout, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error("redis lock occur error", e);
        }
        if (!setIfAbsent) {
            return;
        }
        try {
            task.action();
        } catch (Exception e) {
            log.error("task action occur error", e);
        }
        try {
            redisTemplate.execute(
                    RedisScript.of(
                            "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end",
                            Long.class
                    ),
                    Collections.singletonList(lockKey),
                    clientId);
        } catch (Exception e) {
            log.error("release lock occur error", e);
        }
    }

}

三、不足

  • Redis锁并没有极高的可靠性,Redis一般采用主从方式部署,如果在加锁过程中,Redis主节点挂掉,加锁的指令只在主节点执行完成,但是还未同步到从节点,此时从节点变更为主节点,但是锁信息并没有得到同步,锁的功能就失效了。

  • 上述的方案,如果task执行出现耗时超过锁的超时时间,仍然会出现task并发执行的情况,这种情况需要根据实际业务场景决定,是否允许此类情况发生,如果不允许是否需要增加超时时间,或者提供锁延长超时时间的机制。

标签:task,Redis,setIfAbsent,error,lockKey,redisTemplate,分布式
From: https://www.cnblogs.com/minisdad/p/17521641.html

相关文章

  • Redis详解——性能
    一、rehash会导致操作阻塞吗?如下图,Redis的字典结构中包含了两个哈希表:默认是往ht[0]写数据的,随着数据主键增多,Redis就会触发执行rehash操作了,主要步骤如下:给ht[1]分配更大的空间;将ht[0]的数据拷贝到ht[1];释放ht[0]的空间。如果直接拷贝数据,肯定是会花很长时间的,进一步会导......
  • Jmeter学习之一_连接与测试Redis
    Jmeter学习之一_连接与测试Redis简介下载:https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.6.zip注意事项:Don'tuseGUImodeforloadtesting!,onlyforTestcreationandTestdebugging.Forloadtesting,useCLIMode(wasNONGUI):jmeter-n......
  • 龙蜥7.9 编译安装redis
    1、下载redis安装包2、我一般放在/home下面,redis默认安装在/usr/local/bin/下面,修改目录命令为:makePREFIX=/usr/local/redisinstall#安装编译环境yum-yinstallgcc-c++autoconfautomake#开始安装cd/hometarzxvfredis-7.0.11.tar.gzcdredis-7.0.11/makemakeinsta......
  • Redis详解——内存数据库
    前言在Redis详解——存储中介绍了Redis的基础数据结构,本文我们来看看Redis是如何组织这些数据类型,来构建一个内存数据库的。一、内存数据库以下是Redis数据库的结构:Redis服务器程序所有的数据库都保存在redisService结构体中,其中有个db数组,为redisDb类型,每个元素为一个数据......
  • springboot+token+redis,模拟登录
    登录测试的controller:loginTest.javapackagecom.example.demo.controller;importcom.example.demo.po.ResponseBean;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.web.bind.annotation.*;importjavax.annotation.Resource;i......
  • Redis数据结构——链表
    前言Redis链表为双向无环链表!Redis使用了简单动态字符串,链表、字典(散列表)、跳跃表、整数集合、压缩列表这些数据结构来操作内存。本文继续来分析链表。 链表是一种非常常见的数据结构,在Redis中使用非常广泛,列表对象的底层实现之一就是链表。其它如慢查询,发布订阅,监视器等功......
  • Redis数据结构——字典
    前言字典在Redis中的应用非常广泛,数据库与哈希对象的底层实现就是字典。一、复习散列表1.1散列表散列表(哈希表),其思想主要是基于数组支持按照下标随机访问数据时间复杂度为O(1)的特性。可以说是数组的一种扩展。假设,我们为了方便记录某高校数学专业的所有学生的信息。要求可......
  • Redis数据结构——简单动态字符串SDS
    前言相信用过Redis的人都知道,Redis提供了一个逻辑上的对象系统构建了一个键值对数据库以供客户端用户使用。这个对象系统包括字符串对象、哈希对象、列表对象、集合对象、有序集合对象等。但是Redis面向内存并没有直接使用这些对象。而是使用了简单动态字符串,链表、字典(散列表)、......
  • 微信读书:从Paxos到Zookeeper:分布式一致性原理与实践(阅读摘录)
    微信读书:从Paxos到Zookeeper:分布式一致性原理与实践(阅读摘录)阅读地址CAP理论CAP理论告诉我们,一个分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容错性(P:Partitiontolerance)这三个基本需求,最多只能同时满足其中的两项。BASE理论BASE是Basica......
  • 2023-07-01:redis过期策略都有哪些?LRU 算法知道吗?
    2023-07-01:redis过期策略都有哪些?LRU算法知道吗?答案2023-07-01:缓存淘汰算法(过期策略)当Redis的内存超出物理内存限制时,内存中的数据就会频繁地与磁盘进行交换,这个过程叫做交换(swap)。由于交换的高开销,Redis的性能会急剧下降。对于访问频率较高的Redis实例来说,这样低效的存取效率......