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

相关推荐
Super Rookie3 小时前
MongoDB 自动化脚本安装方案
数据库·mongodb·自动化
Code哈哈笑4 小时前
【MongoDB 基本语法】数据库和集合的基本操作--探秘 MongoDB
数据库·mongodb
恋红尘4 小时前
Redis面试八股
数据库·redis·面试
W.Buffer4 小时前
Kafka零拷贝原理深度解析:从传统拷贝痛点到工作实践优化
分布式·kafka
直有两条腿5 小时前
【数据迁移】HBase Bulkload批量加载原理
大数据·数据库·hbase
没有bug.的程序员5 小时前
服务治理与 API 网关:微服务流量管理的艺术
java·分布式·微服务·架构·wpf
心态特好5 小时前
解锁分布式唯一 ID:技术、实践与最佳方案
分布式·生活
言之。5 小时前
ClickHouse 数据更新策略深度解析:突变操作与最佳实践
服务器·数据库·clickhouse
白衣鸽子6 小时前
数据库高可用设计的灵魂抉择:CAP权衡
数据库·后端