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 实例组成集群,客户端需在所有实例上成功获取锁。
步骤
- 获取当前时间戳
start_time - 依次向 N 个 Redis 实例请求锁(超时时间 < 总耗时)
- 计算获取锁的总耗时
elapsed = current_time - start_time - 如果成功获取锁的实例数 > 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. 最佳实践
关键原则
- 唯一标识:每个锁必须关联唯一值(如 UUID)
- 原子操作:使用 Lua 脚本保证「检查+设置+删除」的原子性
- 合理 TTL:根据业务耗时设置锁超时时间(建议 30~60 秒)
- 网络分区容忍:设置
retryAttempts和retryInterval
避坑指南
• ❌ 避免使用 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 | 企业级复杂需求 | 强一致性 |
根据业务需求选择方案,核心目标:保证锁的原子性、唯一性和容错性。