Redis(66)Redis如何实现分布式锁?

Redis 提供了多种方法来实现分布式锁,确保多个进程或机器能够协调地访问共享资源。以下是详细的实现步骤和代码示例。

1. 基于 SET 命令的分布式锁

获取锁

获取锁的核心是使用 SET 命令,并带上 NXEX 选项:

  • NX(Not eXists): 仅当键不存在时才设置键。
  • EX(EXpire): 设置键的过期时间,防止死锁。
redis 复制代码
# 获取锁示例
SET mylock <lock_value> NX EX 10

Lua 脚本实现

为了更加原子化,可以使用 Lua 脚本:

lua 复制代码
-- 获取锁的 Lua 脚本
local lock_key = KEYS[1]
local lock_value = ARGV[1]
local ttl = tonumber(ARGV[2])

if redis.call("SETNX", lock_key, lock_value) == 1 then
    redis.call("EXPIRE", lock_key, ttl)
    return 1
else
    return 0
end

Redis 命令:

sh 复制代码
redis-cli EVAL "local lock_key = KEYS[1]; local lock_value = ARGV[1]; local ttl = tonumber(ARGV[2]); if redis.call('SETNX', lock_key, lock_value) == 1 then redis.call('EXPIRE', lock_key, ttl); return 1; else return 0; end" 1 mylock lock_value 10

释放锁

释放锁时需要先检查当前锁是否是自己持有的,然后再删除锁:

lua 复制代码
-- 释放锁的 Lua 脚本
local lock_key = KEYS[1]
local lock_value = ARGV[1]

if redis.call("GET", lock_key) == lock_value then
    redis.call("DEL", lock_key)
    return 1
else
    return 0
end

Redis 命令:

sh 复制代码
redis-cli EVAL "local lock_key = KEYS[1]; local lock_value = ARGV[1]; if redis.call('GET', lock_key) == lock_value then redis.call('DEL', lock_key); return 1; else return 0; end" 1 mylock lock_value

2. 基于 Redlock 算法的分布式锁

Redlock 是 Redis 作者提出的一种更加健壮的分布式锁算法,旨在在多个 Redis 实例上实现锁的获取和释放。

获取锁

  1. 获取当前时间。
  2. 依次尝试在多个 Redis 实例上获取锁。
  3. 如果在大部分实例上成功获取锁,并且时间小于有效期,则获取锁成功。
lua 复制代码
-- 基于 Redlock 算法的 Lua 脚本
local lock_key = KEYS[1]
local lock_value = ARGV[1]
local ttl = tonumber(ARGV[2])

local start_time = redis.call('TIME')
local success_count = 0
local majority = math.floor(#redis_instances / 2) + 1

for _, redis_instance in ipairs(redis_instances) do
    if redis_instance.call("SET", lock_key, lock_value, "NX", "PX", ttl) then
        success_count = success_count + 1
    end
end

local elapsed_time = (redis.call('TIME')[1] - start_time[1]) * 1000 + (redis.call('TIME')[2] - start_time[2]) / 1000

if success_count >= majority and elapsed_time < ttl then
    return 1
else
    for _, redis_instance in ipairs(redis_instances) do
        redis_instance.call("DEL", lock_key)
    end
    return 0
end

释放锁

释放锁时需要在所有实例上依次删除锁:

lua 复制代码
-- 基于 Redlock 算法的 Lua 脚本
local lock_key = KEYS[1]
local lock_value = ARGV[1]

for _, redis_instance in ipairs(redis_instances) do
    if redis_instance.call("GET", lock_key) == lock_value then
        redis_instance.call("DEL", lock_key)
    end
end

return 1

3. 使用 Redisson 实现分布式锁

Redisson 是一个基于 Redis 的 Java 客户端,可以方便地进行分布式锁的管理。以下是使用 Redisson 实现分布式锁的示例:

添加 Maven 依赖

xml 复制代码
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.16.1</version>
</dependency>

使用 Redisson 获取和释放锁

java 复制代码
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

import java.util.concurrent.TimeUnit;

public class DistributedLockExample {

    public static void main(String[] args) {
        // 配置 Redis 服务器
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");

        // 创建 Redisson 客户端
        RedissonClient redisson = Redisson.create(config);

        // 获取锁
        RLock lock = redisson.getLock("myLock");
        try {
            // 尝试加锁,等待时间 100ms,锁定时间 10s
            boolean isLocked = lock.tryLock(100, 10000, TimeUnit.MILLISECONDS);
            if (isLocked) {
                try {
                    // 执行需要锁保护的代码
                    System.out.println("Lock acquired!");
                    // 加锁后的操作
                } finally {
                    // 释放锁
                    lock.unlock();
                }
            } else {
                System.out.println("Unable to acquire lock");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 关闭 Redisson 客户端
            redisson.shutdown();
        }
    }
}

总结

通过合理使用 Redis 提供的 SETNX 命令、Lua 脚本、Redlock 算法以及 Redisson 客户端,可以有效地实现分布式锁。这些方法各有优缺点,开发者可以根据具体的应用场景选择最合适的方案来确保分布式系统中的数据一致性和并发控制。

相关推荐
凤山老林3 小时前
新一代Java应用日志可视化与监控系统开源啦
java·后端·开源
Kiri霧10 小时前
Rust开发环境搭建
开发语言·后端·rust
间彧11 小时前
Spring事件监听与消息队列(如Kafka)在实现解耦上有何异同?
后端
间彧11 小时前
Java如何自定义事件监听器,有什么应用场景
后端
叶梅树11 小时前
从零构建A股量化交易工具:基于Qlib的全栈系统指南
前端·后端·算法
间彧11 小时前
CopyOnWriteArrayList详解与SpringBoot项目实战
后端
间彧12 小时前
SpringBoot @FunctionalInterface注解与项目实战
后端
程序员小凯12 小时前
Spring Boot性能优化详解
spring boot·后端·性能优化
Asthenia041212 小时前
问题复盘:飞书OAuth登录跨域Cookie方案探索与实践
后端