前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何优雅的使用Redis实现分布式锁

如何优雅的使用Redis实现分布式锁

作者头像
AI码师
发布2020-11-19 15:57:50
9010
发布2020-11-19 15:57:50
举报

点击上方蓝字关注我们 文末有惊喜哦

为什么要使用分布式锁?

我们在多线程开发过程中,肯定没避免不了使用锁,jdk中也提供了大量的锁功能,但是我们为什么还要手动开发一个分布式锁呢,原因在于我们在传统项目中使用的锁是在同一个进程中的,他们能够相互访问到彼此的资源信息,但是在分布式中,每个项目都是跑在不同的进程中的,他们无法共享资源信息,所以就需要一个能够在不同进程之间进行“通信”的第三方来实现这个功能,那么redis其实就具备这种功能的。

redis实现分布式锁的原理

其实redis实现的原理主要就是某个线程现在redis里面占个坑,然后后面的人进来的时候看见这个坑被占用的话,就一直等待别人释放这个坑或者放弃,释放之后,他再去抢占。

分布式锁的简单实现

#抢占一个坑,使用setnx指令,如果别人创建过,则设置失败,即对应获取锁失败 setnx lock:user_yang true#实现我们的业务逻辑,逻辑处理完之后,调用del指令释放锁 #如果抢占锁的线程挂掉了,锁就会一直不能释放,就会造成资源一直被占用,所以可以设置一个超期时间,到点自动释放锁 expire lock:user_yang 10 del lock:user_yang

问题:如果在设置锁和设置超时时间中间出现问题了,没有设置上超时怎么办,也会无法释放锁?是不是只要保证原子性操作就好了。

为了解决上面的问题,redis2.8中,作者加入了set命令的扩展操作,使得setnx和expire可以一起执行,保证他们的原子操作。

加强版分布式锁

代码语言:javascript
复制
set lock:user_yang true ex 5 nx
...
del lock:user_yang

超时问题

超时问题指的是当我们业务代码执行的时间超过了设置锁的时间,就会导致锁释放被其他线程占用,就会导致临界区的代码不能得到严格的执行,而且当主业务代码执行后,就会去释放这个锁,这个会导致将别人的锁释放,又会引发一系列的问题。

  • 解决方案 在设置锁的时候,随机塞入一个值,删除锁之前先比较这个值,如果和之前设置的值相同,则删除,否则不删除。
  • 原子性问题 因为比较和删除不是原子性操作,会不会引发新的问题,但是redis有没有提供这样的原子性操作指令
  • 解决方案2 使用LUA脚本
代码语言:javascript
复制
# delifequals
if redis.call(“get”,KEYS[1]) == ARGV[1] then
 return redis.call(“del”,KEYS[1])
else
 return 0
end

java实现分布式锁

  • 引入依赖
代码语言:javascript
复制
 <dependency
     <groupIdredis.clients</groupId
     <artifactIdjedis</artifactId
     <version2.9.0</version
 </dependency
  • 公用常量
代码语言:javascript
复制
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";
private static final Long RELEASE_SUCCESS = 1L;
  • 获取锁
代码语言:javascript
复制
     /**
     * 尝试获取分布式锁
     * @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;
    }

  • 释放锁
代码语言:javascript
复制
    /**
     * 释放分布式锁
     * @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;
    }
  • 客户端演示
代码语言:javascript
复制
 @Test
    public void getLock(){
        Jedis jedis = new Jedis("172.16.51.129",6379);
        String requestId = UUID.randomUUID().toString();
        boolean boolSuccess = RedisUtils.tryGetDistributedLock(jedis, "lock:user_yang",requestId, 10);
        if(boolSuccess){
            System.out.println("成功获取锁");
        }else{
            System.out.println("获取锁失败");
        }
        boolSuccess = RedisUtils.tryGetDistributedLock(jedis, "lock:user_yang", requestId, 10);
        if(boolSuccess){
            System.out.println("成功获取锁");
        }else{
            System.out.println("获取锁失败");
        }
        boolSuccess = RedisUtils.releaseDistributedLock(jedis, "lock:user_yang", requestId);
        if(boolSuccess){
            System.out.println("释放锁成功");
        }else{
            System.out.println("释放锁失败");
        }
    }

认真读一本书

按照惯例,文末必推一本书,这本书我也买过纸质书,本系列所有内容都是围绕本书结合自身学习进行总结归纳的。

如果需要电子版,也可以关注公众号“乐哉开讲“,后台点击“干货领取“。「当然,写书不易,条件允许的话,还请支持正版。」


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-10-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 乐哉开讲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么要使用分布式锁?
  • redis实现分布式锁的原理
  • 分布式锁的简单实现
  • 加强版分布式锁
  • 超时问题
  • java实现分布式锁
相关产品与服务
云数据库 Redis®
腾讯云数据库 Redis®(TencentDB for Redis®)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档