Redis的分布式锁实现

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容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
目录
相关文章
|
6月前
|
存储 NoSQL 算法
Redis (分布式锁)
Redis (分布式锁)
230 0
|
6月前
|
NoSQL Java 大数据
介绍redis分布式锁
分布式锁是解决多进程在分布式环境中争夺资源的问题,与本地锁相似但适用于不同进程。以Redis为例,通过`setIfAbsent`实现占锁,加锁同时设置过期时间避免死锁。然而,获取锁与设置过期时间非原子性可能导致并发问题,解决方案是使用`setIfAbsent`的超时参数。此外,释放锁前需验证归属,防止误删他人锁,可借助Lua脚本确保原子性。实际应用中还有锁续期、重试机制等复杂问题,现成解决方案如RedisLockRegistry和Redisson。
67 0
|
6月前
|
NoSQL 关系型数据库 MySQL
Redis实现分布式锁
Redis实现分布式锁
42 0
|
6月前
|
缓存 监控 NoSQL
基于redis的分布式锁
基于redis的分布式锁
46 0
|
6月前
|
NoSQL Java 关系型数据库
浅谈Redis实现分布式锁
浅谈Redis实现分布式锁
60 0
|
NoSQL Java 程序员
Redis实现分布式锁(2)
Redis实现分布式锁(2)
|
存储 NoSQL 算法
Redis的分布式锁详解
Redis的分布式锁详解
Redis的分布式锁详解
|
负载均衡 NoSQL Java
基于redis和shedlock实现分布式锁(超简单)
基于redis和shedlock实现分布式锁(超简单)
449 0
|
存储 消息中间件 NoSQL
Redis分布式锁真的安全吗?
Redis分布式锁真的安全吗?
|
缓存 NoSQL Java
redis分布式锁(1)
随着业务发展的需要,原单体单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!
redis分布式锁(1)