分布式锁实现--Redis(Java)

本文深入探讨Redis实现分布式锁的原理,包括SET命令的多种参数、锁的原子性操作及Lua脚本的应用,同时解决锁释放和Redis节点故障等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分布式锁,是指在分布式的部署环境下,通过锁机制来让多客户端互斥的对共享资源进行访问。

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

  1. 基于关系型数据库,如MySQL
  2. 基于缓存数据库,如Redis
  3. 基于Zookeeper

此处对Redis如何实现分布式锁进行一下讲解

一、 实现原理

主要利用Redis的几个特点:

  1. "SET key value [EX seconds] [PX milliseconds] [NX|XX] "命令 如:SET key value EX 10 NX 若key不存在设置一个10秒过期的key-value
  2. "DEL" 命令 释放锁即删除对应key
  3. Redis是单线程的, 保证多个锁的竞争者只有一个能够创建key成功即获取到锁

SET key value [EX seconds] [PX milliseconds] [NX|XX] 命令:

EX seconds将键的过期时间设置为 seconds 秒。 执行 SET key value EX seconds 的效果等同于执行 SETEX key seconds value
PX milliseconds将键的过期时间设置为 milliseconds 毫秒。 执行 SET key value PX milliseconds 的效果等同于执行 PSETEX key milliseconds value
NX 只在键不存在时, 才对键进行设置操作。 执行 SET key value NX 的效果等同于执行 SETNX key value 。
XX 只在键已经存在时, 才对键进行设置操作。

二、可能存在的问题

1.锁的释放

A获取锁之后, 执行时间为15s, 锁的过期时间为10s, 当A执行完之前, B获取到了锁, A执行完之后使用DEL强制删key, 删除掉的其实是B获取到的锁

解决方案: 们可以在设置key的时候将value设置为一个唯一值uniqueValue(可以是随机值、UUID、或者机器号+线程号的组合、签名等)。当解锁时,也就是删除key的时候先判断一下key对应的value是否等于先前设置的值,如果相等才能删除key

但我们要保证 判断value是否相等的操作 与 删除 操作 的原子性

// GET  与  DEL 是两个分开的操作
if uniqueKey == GET(key) {
	DEL key
}

Lua脚本的原子性,在Redis执行该脚本的过程中,其他客户端的命令都需要等待该Lua脚本执行完才能执行

//其中ARGV[1]表示设置key时指定的唯一值。
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

2.Redis的master节点宕机

  1. 线程A在master节点拿到了锁。
  2. master节点在把A创建的key写入slave之前宕机了。
  3. slave变成了master节点。
  4. 线程B也得到了和A还持有的相同的锁。(因为原来的slave里面还没有A持有锁的信息)

三、代码示例

public boolean lock(String lockKey, String uniqueValue, int seconds){
    SetParams params = new SetParams();
    params.nx().ex(seconds);
    String result = jedis.set(lockKey, uniqueValue, params);
    if ("OK".equals(result)) {
        return true;
    }
    return false;
}

public boolean unlock(String lockKey, String uniqueValue){
    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(uniqueValue));
    if (result.equals(1)) {
        return true;
    }
    return false;
}

转自:

https://blog.youkuaiyun.com/u013256816/article/details/93305532

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值