Redis 分布式锁实现教程

111 阅读3分钟

Redis 分布式锁实现教程

1. 基础版:使用 SET 命令实现锁

核心命令

SET lock_key unique_value NX PX 30000

NX: 仅当键不存在时设置(保证原子性) • PX 30000: 设置键的过期时间为 30 秒(防止死锁)

释放锁(Lua 脚本)

-- 释放锁时校验唯一值,防止误删其他客户端的锁
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end

代码示例(Python)

import redis
import uuid

client = redis.Redis(host='localhost', port=6379)

def acquire_lock(lock_key):
    unique_id = str(uuid.uuid4())
    # 尝试获取锁
    result = client.set(lock_key, unique_id, nx=True, px=30000)
    return unique_id if result else None

def release_lock(lock_key, unique_id):
    script = """
    if redis.call("GET", KEYS[1]) == ARGV[1] then
        return redis.call("DEL", KEYS[1])
    else
        return 0
    end
    """
    client.eval(script, 1, lock_key, unique_id)

优点

简单易用,适合低并发场景。

缺点

• 主从同步延迟可能导致锁丢失(参考前文问题) • 需要自行处理锁续期


2. 进阶版:锁续期(看门狗机制)

问题

业务执行时间可能超过锁的 TTL(如 30 秒),导致锁提前释放。

解决方案

启动后台线程定期续期:

import threading

def renew_lock(lock_key, unique_id):
    while True:
        # 每 10 秒续期一次
        client.expire(lock_key, 30)
        time.sleep(10)

# 获取锁后启动续期线程
unique_id = acquire_lock(lock_key)
if unique_id:
    threading.Thread(target=renew_lock, args=(lock_key, unique_id)).start()

3. 高可用版:Redlock 算法

核心思想

通过多个独立的 Redis 实例组成集群,客户端需在所有实例上成功获取锁。

步骤

  1. 获取当前时间戳 start_time
  2. 依次向 N 个 Redis 实例请求锁(超时时间 < 总耗时)
  3. 计算获取锁的总耗时 elapsed = current_time - start_time
  4. 如果成功获取锁的实例数 > N/2 且 elapsed < TTL,则认为锁获取成功

代码示例(伪代码)

def redlock_acquire(resource, ttl):
    quorum = (N // 2) + 1
    start_time = time.time()
    acquired_locks = 0

    for instance in redis_instances:
        if acquire_single_lock(instance, resource, ttl):
            acquired_locks += 1

    elapsed = time.time() - start_time
    if acquired_locks >= quorum and elapsed < ttl:
        return True
    else:
        # 释放所有已获取的锁
        redlock_release(resource)
        return False

优点

容忍主从故障,适合高可用场景。

缺点

实现复杂,性能开销大。


4. 生产级方案:Redisson

核心功能

自动续期、看门狗机制、Redlock 支持、多种锁类型(可重入锁、公平锁等)。

Maven 依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.16.8</version>
</dependency>

代码示例

Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);

// 获取分布式锁
RLock lock = redisson.getLock("myLock");
lock.lock(30, TimeUnit.SECONDS); // 自动续期

try {
    // 业务逻辑
} finally {
    lock.unlock();
}

5. 最佳实践

关键原则

  1. 唯一标识:每个锁必须关联唯一值(如 UUID)
  2. 原子操作:使用 Lua 脚本保证「检查+设置+删除」的原子性
  3. 合理 TTL:根据业务耗时设置锁超时时间(建议 30~60 秒)
  4. 网络分区容忍:设置 retryAttemptsretryInterval

避坑指南

• ❌ 避免使用 SETNX + EXPIRE 分步操作(非原子性) • ❌ 不要依赖 Redis 事务(WATCH 命令无法完全解决锁竞争) • ✅ 使用 Redlock 或 Redisson 处理高可用场景 • ✅ 监控锁竞争指标(如 redis.lock.acquisitions


6. 扩展场景

RediSQL

通过 SQL 语法操作锁:

LOCK my_lock IN EXCLUSIVE MODE NOWAIT;
-- 业务逻辑
COMMIT;

Redis 7.0 新特性

支持 WAIT 命令等待副本同步:

WAIT 2 1000  # 等待 2 个副本确认,超时 1000ms

总结

方案适用场景一致性保障
基础 SETNX单机/低并发最终一致性
看门狗+续期防止误释放锁最终一致性
Redlock高可用集群强一致性
Redisson企业级复杂需求强一致性

根据业务需求选择方案,核心目标:保证锁的原子性、唯一性和容错性