Redis的分布式锁实现

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis的分布式锁实现

 分布式锁一般有三种实现方式:

1. 数据库乐观锁;

2. 基于Redis的分布式锁;

3. 基于ZooKeeper的分布式锁。

分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问

分布式锁的使用场景: 线程间并发问题和进程间并发问题都是可以通过分布式锁解决的,但是强烈不建议这样做!

分布式锁的实现(Redis)

几个要用到的redis命令:

setnx(key, value):“set if not exits”,若该key-value不存在,则成功加入缓存并且返回1,否则返回0。

get(key):获得key对应的value值,若不存在则返回nil。

getset(key, value):先获取key对应的value值,若不存在则返回nil,然后将旧的value更新为新的value。

expire(key, seconds):设置key-value的有效期为seconds秒。

代码实现:

依赖引入:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

image.gif

public class RedisPool { 
     private static JedisPool pool;//jedis连接池 
     private static int maxTotal = 20;//最大连接数 
     private static int maxIdle = 10;//最大空闲连接数 
     private static int minIdle = 5;//最小空闲连接数 
     private static boolean testOnBorrow = true;//在取连接时测试连接的可用性 
     private static boolean testOnReturn = false;//再还连接时不测试连接的可用性
     static {
         initPool();//初始化连接池
     }
     public static Jedis getJedis(){
         return pool.getResource();
     }
     public static void close(Jedis jedis){
         jedis.close();
     }
     private static void initPool(){
         JedisPoolConfig config = new JedisPoolConfig();
         config.setMaxTotal(maxTotal);
         config.setMaxIdle(maxIdle);
         config.setMinIdle(minIdle);
         config.setTestOnBorrow(testOnBorrow);
         config.setTestOnReturn(testOnReturn);
         config.setBlockWhenExhausted(true);
         pool = new JedisPool(config, “127.0.0.1”, 6379, 5000, “liqiyao”);
     }
 }

image.gif

Jedis的api进行封装:

public class RedisPoolUtil { 
     private RedisPoolUtil(){} 
     private static RedisPool redisPool; 
     public static String get(String key){
         Jedis jedis = null;
         String result = null;
         try {
             jedis = RedisPool.getJedis();
             result = jedis.get(key);
         } catch (Exception e){
             e.printStackTrace();
         } finally {
             if (jedis != null) {
                 jedis.close();
             }
             return result;
         }
     }
     public static Long setnx(String key, String value){ 
         Jedis jedis = null;
         Long result = null;
         try {
             jedis = RedisPool.getJedis();
             result = jedis.setnx(key, value);
         } catch (Exception e){
             e.printStackTrace();
         } finally {
             if (jedis != null) {
                 jedis.close();
             }
             return result;
         }
     }
     public static String getSet(String key, String value){
         Jedis jedis = null;
         String result = null;
         try {
             jedis = RedisPool.getJedis();
             result = jedis.getSet(key, value);
         } catch (Exception e){
             e.printStackTrace();
         } finally {
             if (jedis != null) {
                 jedis.close();
             }
             return result;
         }
     }
     public static Long expire(String key, int seconds){ 
         Jedis jedis = null;
         Long result = null;
         try {
             jedis = RedisPool.getJedis();
             result = jedis.expire(key, seconds);
         } catch (Exception e){
             e.printStackTrace();
         } finally {
             if (jedis != null) {
                 jedis.close();
             }
             return result;
         }
     }
     public static Long del(String key){ 
         Jedis jedis = null;
         Long result = null;
         try {
             jedis = RedisPool.getJedis();
             result = jedis.del(key);
         } catch (Exception e){
             e.printStackTrace();
         } finally {
             if (jedis != null) {
                 jedis.close();
             }
             return result;
         }
     }
 }

image.gif

分布式锁工具类:

public class DistributedLockUtil { 
     private DistributedLockUtil(){
     }
     public static boolean lock(String lockName){//lockName可以为共享变量名,也可以为方法名,主要是用于模拟锁信息
         System.out.println(Thread.currentThread() + “开始尝试加锁!”);
         Long result = RedisPoolUtil.setnx(lockName, String.valueOf(System.currentTimeMillis() + 5000));
         if (result != null && result.intValue() == 1){
             System.out.println(Thread.currentThread() + “加锁成功!”);
             RedisPoolUtil.expire(lockName, 5);
             System.out.println(Thread.currentThread() + “执行业务逻辑!”);
             RedisPoolUtil.del(lockName);
             return true;
         } else {
             String lockValueA = RedisPoolUtil.get(lockName);
             if (lockValueA != null && Long.parseLong(lockValueA) >= System.currentTimeMillis()){
                 String lockValueB = RedisPoolUtil.getSet(lockName, String.valueOf(System.currentTimeMillis() + 5000));
                 if (lockValueB == null || lockValueB.equals(lockValueA)){
                     System.out.println(Thread.currentThread() + “加锁成功!”);
                     RedisPoolUtil.expire(lockName, 5);
                     System.out.println(Thread.currentThread() + “执行业务逻辑!”);
                     RedisPoolUtil.del(lockName);
                     return true;
                 } else {
                     return false;
                 }
             } else {
                 return false;
             }
         }
     }
 }

image.gif

加锁代码:

public class RedisTool {
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";
    /**
     * 尝试获取分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @param expireTime 超期时间
     * @return 是否获取成功
     */
    public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }
}

image.gif

解锁代码:

public class RedisTool {
    private static final Long RELEASE_SUCCESS = 1L;
    /**
     * 释放分布式锁
     * @param jedis Redis客户端
     * @param lockKey 锁
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
        if (RELEASE_SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }
}

image.gif

基于redisson分布式锁

配置文件:

<dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson</artifactId>
            <version>3.11.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

image.gif

@Autowired
    private RedissonClient redissonClient;
    @RequestMapping("/redissonLock")
    public String redissonLock() throws InterruptedException { 
        RLock rLock = redissonClient.getLock("order");
        log.info("我进入了方法");
        rLock.lock(30, TimeUnit.SECONDS);
        log.info("获得了锁");
        Thread.sleep(15000);
        log.info("释放了锁");
        rLock.unlock();
        log.info("方法执行完成");
        return "方法执行完成";
    }

image.gif


文章下方有交流学习区!一起学习进步!也可以前往官网,加入官方微信交流群

创作不易,如果觉得文章不错,可以点赞收藏评论

你的支持和鼓励是我创作的动力❗❗❗

官网Doker 多克;官方旗舰店首页-Doker 多克-淘宝网  全品优惠

相关实践学习
基于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
目录
相关文章
|
11天前
|
NoSQL 算法 Java
探讨redis分布式锁
探讨redis分布式锁
18 1
|
18天前
|
缓存 NoSQL 安全
玩转Redis!非常强大的Redisson分布式集合,少写60%代码
Redisson是Java的Redis客户端,提供实时数据平台服务,简化了分布式环境下的数据管理。它包含RList、RSet、RMap等分布式集合,支持ConcurrentMap和Set接口,确保线程安全和数据一致性。例如,RMap实现了本地缓存和监听器功能,允许数据监听和本地加速读取。此外,还提供了RSet的排序和去重功能,以及RQueue和RBlockingQueue等队列实现,支持阻塞操作。通过Redisson,开发者能轻松处理分布式系统的数据同步和操作。
|
1月前
|
监控 NoSQL 算法
探秘Redis分布式锁:实战与注意事项
本文介绍了Redis分区容错中的分布式锁概念,包括利用Watch实现乐观锁和使用setnx防止库存超卖。乐观锁通过Watch命令监控键值变化,在事务中执行修改,若键值被改变则事务失败。Java代码示例展示了具体实现。setnx命令用于库存操作,确保无超卖,通过设置锁并检查库存来更新。文章还讨论了分布式锁存在的问题,如客户端阻塞、时钟漂移和单点故障,并提出了RedLock算法来提高可靠性。Redisson作为生产环境的分布式锁实现,提供了可重入锁、读写锁等高级功能。最后,文章对比了Redis、Zookeeper和etcd的分布式锁特性。
242 16
探秘Redis分布式锁:实战与注意事项
|
22天前
|
缓存 NoSQL 关系型数据库
【Redis】 浅谈分布式架构
【Redis】 浅谈分布式架构
|
5天前
|
负载均衡 NoSQL 关系型数据库
Redis分布式锁学习总结
Redis分布式锁学习总结
12 0
|
20天前
|
存储 缓存 NoSQL
了解Redis,第一弹,什么是RedisRedis主要适用于分布式系统,用来用缓存,存储数据,在内存中存储那么为什么说是分布式呢?什么叫分布式什么是单机架构微服务架构微服务的本质
了解Redis,第一弹,什么是RedisRedis主要适用于分布式系统,用来用缓存,存储数据,在内存中存储那么为什么说是分布式呢?什么叫分布式什么是单机架构微服务架构微服务的本质
|
28天前
|
存储 NoSQL 算法
Redis (分布式锁)
Redis (分布式锁)
200 0
|
1月前
|
NoSQL Java 大数据
介绍redis分布式锁
分布式锁是解决多进程在分布式环境中争夺资源的问题,与本地锁相似但适用于不同进程。以Redis为例,通过`setIfAbsent`实现占锁,加锁同时设置过期时间避免死锁。然而,获取锁与设置过期时间非原子性可能导致并发问题,解决方案是使用`setIfAbsent`的超时参数。此外,释放锁前需验证归属,防止误删他人锁,可借助Lua脚本确保原子性。实际应用中还有锁续期、重试机制等复杂问题,现成解决方案如RedisLockRegistry和Redisson。
|
1月前
|
NoSQL Java 关系型数据库
【Redis系列笔记】分布式锁
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
432 2
|
1月前
|
NoSQL Java Redis
redis分布式锁
redis分布式锁