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

相关推荐
李广坤13 小时前
MySQL 大表字段变更实践(改名 + 改类型 + 改长度)
数据库
初次攀爬者2 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
爱可生开源社区2 天前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba
随逸1772 天前
《从零搭建NestJS项目》
数据库·typescript
加号32 天前
windows系统下mysql多源数据库同步部署
数据库·windows·mysql
シ風箏2 天前
MySQL【部署 04】Docker部署 MySQL8.0.32 版本(网盘镜像及启动命令分享)
数据库·mysql·docker
李慕婉学姐2 天前
Springboot智慧社区系统设计与开发6n99s526(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
百锦再2 天前
Django实现接口token检测的实现方案
数据库·python·django·sqlite·flask·fastapi·pip
tryCbest2 天前
数据库SQL学习
数据库·sql
jnrjian2 天前
ORA-01017 查找机器名 用户名 以及library cache lock 参数含义
数据库·oracle