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 客户端,可以有效地实现分布式锁。这些方法各有优缺点,开发者可以根据具体的应用场景选择最合适的方案来确保分布式系统中的数据一致性和并发控制。

相关推荐
雨中飘荡的记忆4 小时前
秒杀系统设计与实现
java·redis·lua
b***46244 小时前
从 SQL 语句到数据库操作
数据库·sql·oracle
Q***f6354 小时前
后端数据库性能优化的8个工具推荐
数据库·性能优化
小坏讲微服务4 小时前
Spring Cloud Alibaba 整合 Scala 教程完整使用
java·开发语言·分布式·spring cloud·sentinel·scala·后端开发
一水鉴天4 小时前
整体设计 定稿 之1 devOps 中台的 结论性表述(豆包助手)
服务器·数据库·人工智能
pale_moonlight4 小时前
九、Spark基础环境实战((上)虚拟机安装Scala与windows端安装Scala)
大数据·分布式·spark
q***07145 小时前
Spring Boot 中使用 @Transactional 注解配置事务管理
数据库·spring boot·sql
m***92385 小时前
【SQL】MySQL中的字符串处理函数:concat 函数拼接字符串,COALESCE函数处理NULL字符串
数据库·sql·mysql
!停5 小时前
VS实用调试技巧
数据库
BD_Marathon5 小时前
【Zookeeper】CAP理论——CAP介绍
linux·分布式·zookeeper