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

相关推荐
Yeats_Liao12 小时前
时序数据库系列(六):物联网监控系统实战
数据库·后端·物联网·时序数据库
xrkhy13 小时前
canal1.1.8+mysql8.0+jdk17+redis的使用
android·redis·adb
珊珊而川13 小时前
MAC-SQL:黄金标准错误
数据库·sql
Luke Ewin13 小时前
内网私有化分布式集群部署语音识别接口
人工智能·分布式·语音识别·asr·funasr·通话语音质检·区分说话人
一 乐14 小时前
智慧党建|党务学习|基于SprinBoot+vue的智慧党建学习平台(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·学习
小马爱打代码15 小时前
实战:分布式开源监控Zabbix
分布式·开源·zabbix
lcanfly15 小时前
Mysql作业4
数据库·mysql
筑梦之人16 小时前
Spark-3.5.7文档2 - RDD 编程指南
大数据·分布式·spark
喜欢踢足球的老罗16 小时前
认证与授权:详解大型系统中用户中心与RBAC的共生关系
数据库·rbac
zhaomx198916 小时前
Spring 事务管理 Transaction rolled back because it has been marked as rollback-only
数据库·spring