通过lua实现redis 分布式锁的项目实践

 更新时间:2026年02月04日 10:28:10   作者:哈里谢顿  
在Redis中使用Lua脚本实现分布式锁 是一种高效、原子、安全的方式,本文就来介绍一下通过lua实现redis分布式锁,具有一定的参考价值,感兴趣的可以了解一下

在 Redis 中使用 Lua 脚本实现分布式锁 是一种高效、原子、安全的方式。由于 Lua 脚本在 Redis 中是单线程、原子执行的,可以避免多个客户端在加锁/解锁时出现竞态条件(Race Condition)。

下面将为你提供一个 完整、生产可用的 Redis 分布式锁 Lua 实现方案,包含:

  • 加锁(lock)
  • 解锁(unlock)
  • 自动续期(可选)
  • 防止误删他人锁
  • 支持可重入(可选)

✅ 一、核心设计原则

  1. 锁的唯一标识:每个客户端使用唯一的 value(如 UUID)作为锁标识,防止 A 客户端释放 B 客户端的锁。
  2. 原子性操作:加锁和解锁必须通过 Lua 脚本保证原子性。
  3. 自动过期:设置 TTL(如 30 秒),防止死锁。
  4. 可重入支持(可选) :同一个客户端可多次加锁(需记录重入次数)。

本文先实现 基础版(不可重入) ,再给出 可重入增强版

✅ 二、基础版:不可重入分布式锁

🔐 1. 加锁脚本(lock.lua)

-- KEYS[1] = 锁的 key(如 "my_lock")
-- ARGV[1] = 锁的 value(唯一标识,如 UUID)
-- ARGV[2] = 过期时间(毫秒,如 "30000")

local key = KEYS[1]
local value = ARGV[1]
local ttl = tonumber(ARGV[2])

-- 尝试加锁:只有当 key 不存在时才设置成功
if redis.call("SET", key, value, "NX", "PX", ttl) then
    return 1  -- 加锁成功
else
    return 0  -- 加锁失败(已被占用)
end

✅ SET key value NX PX ttl 是 Redis 2.6.12+ 的原子命令:

  • NX:仅当 key 不存在时设置
  • PX:设置过期时间(毫秒)

🔓 2. 解锁脚本(unlock.lua)

-- KEYS[1] = 锁的 key
-- ARGV[1] = 锁的 value(用于验证是否是自己的锁)

local key = KEYS[1]
local value = ARGV[1]

-- 获取当前锁的 value
local current_value = redis.call("GET", key)

-- 如果存在且等于自己的 value,则删除
if current值 == value then
    redis.call("DEL", key)
    return 1  -- 解锁成功
else
    return 0  -- 不是自己的锁,拒绝删除
end

⚠️ 注意:不能直接 DEL,否则可能误删别人的锁!

✅ 三、Python 示例(使用 redis-py)

import redis
import uuid
import time

# 初始化 Redis 连接
r = redis.StrictRedis(host='localhost', port=6379, decode_responses=True)

# 加载 Lua 脚本(推荐使用 script_load + evalsha 提高性能)
lock_script = r.register_script("""
if redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2]) then
    return 1
else
    return 0
end
""")

unlock_script = r.register_script("""
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end
""")

def acquire_lock(lock_name, acquire_timeout=10, lock_timeout=30):
    """获取分布式锁"""
    identifier = str(uuid.uuid4())
    lock_timeout_ms = int(lock_timeout * 1000)
    end_time = time.time() + acquire_timeout

    while time.time() < end_time:
        # 执行 Lua 脚本尝试加锁
        if lock_script(keys=[lock_name], args=[identifier, lock_timeout_ms]):
            return identifier  # 返回锁标识
        time.sleep(0.01)  # 短暂等待后重试

    return False

def release_lock(lock_name, identifier):
    """释放分布式锁"""
    unlock_script(keys=[lock_name], args=[identifier])

# ===== 使用示例 =====
lock_key = "order:123:lock"

# 尝试获取锁
lock_id = acquire_lock(lock_key, acquire_timeout=5, lock_timeout=30)
if lock_id:
    try:
        print("✅ 获取锁成功,执行业务逻辑...")
        time.sleep(5)  # 模拟业务处理
    finally:
        # 释放锁
        release_lock(lock_key, lock_id)
        print("🔓 锁已释放")
else:
    print("❌ 获取锁超时")

✅ 四、增强版:支持可重入锁(Reentrant Lock)

适用于同一线程/协程需要多次进入临界区的场景。

设计思路:

  • 使用 Hash 结构 存储锁:

    • field = 客户端 ID(UUID)
    • value = 重入次数(counter)
  • 设置整体 TTL

🔐 可重入加锁脚本(reentrant_lock.lua)

-- KEYS[1] = 锁 key
-- ARGV[1] = 客户端 ID
-- ARGV[2] = 锁过期时间(毫秒)

local key = KEYS[1]
local client_id = ARGV[1]
local ttl = tonumber(ARGV[2])

-- 检查是否已持有锁
local current_client = redis.call("HGET", key, client_id)

if current_client then
    -- 已持有:重入次数 +1,刷新 TTL
    redis.call("HINCRBY", key, client_id, 1)
    redis.call("PEXPIRE", key, ttl)
    return 1
else
    -- 未持有:尝试获取锁
    if redis.call("HLEN", key) == 0 then
        -- 锁空闲,创建
        redis.call("HMSET", key, client_id, 1)
        redis.call("PEXPIRE", key, ttl)
        return 1
    else
        -- 锁被他人持有
        return 0
    end
end

🔓 可重入解锁脚本(reentrant_unlock.lua)

-- KEYS[1] = 锁 key
-- ARGV[1] = 客户端 ID

local key = KEYS[1]
local client_id = ARGV[1]

-- 检查是否持有锁
local counter = redis.call("HGET", key, client_id)

if not counter then
    return 0  -- 未持有锁
end

counter = tonumber(counter)

if counter > 1 then
    -- 重入次数 >1,减1
    redis.call("HINCRBY", key, client_id, -1)
    return 1
else
    -- 最后一次,删除整个 key
    redis.call("DEL", key)
    return 1
end

💡 使用方式与基础版类似,只需替换 Lua 脚本。

✅ 五、注意事项与最佳实践

问题解决方案
锁过期但业务未完成使用“看门狗”(Watchdog)线程自动续期(如 Redisson 的机制)
主从切换导致锁丢失使用 Redlock 算法(多 Redis 实例),但争议较大;更推荐强一致存储(如 etcd)
Lua 脚本调试困难在开发环境打印日志(redis.log(redis.LOG_WARNING, "msg"))
性能优化使用 SCRIPT LOAD + EVALSHA 避免每次传输脚本

📌 重要提醒:
Redis 分布式锁 不保证绝对安全(尤其在主从异步复制场景)。
对一致性要求极高的场景(如金融),建议使用 etcd / ZooKeeper。

✅ 六、总结

功能基础版可重入版
原子加锁
防止误删
自动过期
同客户端多次加锁
复杂度

推荐:

  • 普通场景 → 用 基础版
  • 需要递归调用/嵌套锁 → 用 可重入版

通过以上 Lua 脚本 + 客户端封装,你可以在 Redis 中实现一个高效、安全、可靠的分布式锁系统。

到此这篇关于通过lua实现redis 分布式锁的项目实践的文章就介绍到这了,更多相关lua redis 分布式锁内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • redis-benchmark并发压力测试的问题解析

    redis-benchmark并发压力测试的问题解析

    这篇文章主要介绍了redis-benchmark并发压力测试的问题解析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01
  • 浅谈redis内存数据的持久化方式

    浅谈redis内存数据的持久化方式

    这篇文章主要介绍了浅谈redis内存数据的持久化方式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • 浅谈Redis缓存击穿、缓存穿透、缓存雪崩的解决方案

    浅谈Redis缓存击穿、缓存穿透、缓存雪崩的解决方案

    这篇文章主要介绍了浅谈Redis缓存击穿、缓存穿透、缓存雪崩的解决方案,缓存是分布式系统中的重要组件,主要解决在高并发、大数据场景下,热点数据访问的性能问题,需要的朋友可以参考下
    2023-03-03
  • Redis 延迟双删的实现示例

    Redis 延迟双删的实现示例

    延迟双删是在Cache-Aside基础上,为了解决并发读写导致缓存回填旧值问题而设计的方案,本文就来详细的介绍一下Redis 延迟双删的实现示例,感兴趣的可以了解一下
    2026-05-05
  • Redis 高可用与集群原理深度解析

    Redis 高可用与集群原理深度解析

    Redis高可用机制涵盖主从复制、哨兵自动故障转移及Cluster集群分片与高可用,依赖Gossip协议实现节点通信与状态同步,源码解析了故障转移流程,为后续持久化内容奠定基础,本文给大家介绍Redis高可用与集群原理,感兴趣的朋友跟随小编一起看看吧
    2025-09-09
  • SpringBoot整合Redis实现序列化存储Java对象的操作方法

    SpringBoot整合Redis实现序列化存储Java对象的操作方法

    这篇文章主要介绍了SpringBoot整合Redis实现序列化存储Java对象,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • 远程连接阿里云服务器上的redis报错的问题解决

    远程连接阿里云服务器上的redis报错的问题解决

    本文主要介绍了远程连接阿里云服务器上的redis报错的问题,出现 Redis Client On Error: Error: connect ECONNREFUSED 47.100.XXX.XX:6379 错误,下面就来介绍一下解决方法,感兴趣的可以了解一下
    2025-04-04
  • Redis @type坑的解决

    Redis @type坑的解决

    新建一个对象存入redis中,对象中会出现一个字段@type,本文主要介绍了Redis @type坑的解决,具有一定的参考价值,感兴趣的可以了解一下
    2024-06-06
  • Redis String 类型和 Hash 类型学习笔记与总结

    Redis String 类型和 Hash 类型学习笔记与总结

    这篇文章主要介绍了Redis String 类型和 Hash 类型学习笔记与总结,本文分别对String 类型的一些方法和Hash 类型做了详细介绍,需要的朋友可以参考下
    2015-06-06
  • Redis的数据复制过程详解

    Redis的数据复制过程详解

    Redis 的复制功能分为同步(sync)和命令传播(command propagate)这两个操作,这篇文章主要介绍了Redis的数据复制,需要的朋友可以参考下
    2022-12-12

最新评论