Redis 分布式锁的正确使用方式

5 篇文章 0 订阅

随着业务越来越复杂,应用服务都会朝着分布式、集群方向部署,而分布式CAP原则告诉我们,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。
很多场景下,需要通过分布式事务或者是分布式锁来保证数据的最终一致性。
分布式锁的实现方式主要有三种,

  • 基于数据库的分布式锁
  • 基于Zookeeper的分布式锁
  • 基于Redis的分布式锁

本篇文章主要展示如何通过redis来实现分布式锁。

可靠性

  1. 互斥性。任意时刻都只能有一个客户端持有锁
  2. 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  3. 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
  4. 加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

加锁代码

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;
    }
}

下面一一解释5个参数的含义:

  1. 第一个为锁的key,用来表示唯一性。
  2. 第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。
  3. 第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
  4. 第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。
  5. 第五个为time,与第四个参数相呼应,代表key的过期时间。

解锁代码示例

  /**
     * 释放分布式锁
     * @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;
 
    }

这里用到了Redis官方提供的解锁代码,通过Lua语言来确保对redis操作的原子性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis分布式锁可以通过多种方式实现。其中一种方式使用Redisson分布式锁Redisson解决了「过期释放,业务没执行完」的问题。它通过在加时启动一个后台线程,每隔一段时间检查是否还被持有,如果是,则延长的生存时间。这样可以确保在业务逻辑执行期间不会过期。\[1\] 除了Redisson,还有其他的分布式锁实现方式。其中一种方式使用SETNX + EXPIRE命令,通过设置一个键值对来表示的状态,并设置过期时间来自动释放。另一种方式使用Lua脚本,通过原子性的执行SETNX和EXPIRE两条指令来实现加和设置过期时间。还有一种方式使用Redis的扩展命令SET EX PX NX,通过设置过期时间和唯一随机值来实现加。此外,还有Redlock和Redission等开源框架可以实现多机的分布式锁。\[2\] 需要注意的是,分布式锁的实现方式需要考虑一些问题,比如加后业务逻辑还未执行完成已经过期,这会导致其他客户端拿到。如果是单节点,这个问题不大,但是在集群环境下,加首先会落盘到master节点,然后再复制到slave节点。如果在复制之前master节点挂掉,就会导致丢失的问题。为了解决这些问题,Redis官方推荐使用Redisson分布式锁。\[3\] #### 引用[.reference_title] - *1* *2* [Redis实现分布式锁的7种方案](https://blog.csdn.net/qszfly/article/details/126100421)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v4^insert_chatgpt"}} ] [.reference_item] - *3* [Redis分布式锁正确实现方式](https://blog.csdn.net/yaomingyang/article/details/104965554)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v4^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值