Redis--基础知识点--24--分布式锁

本文详细介绍了如何使用Redis实现分布式锁,包括简单的加锁和释放锁操作,以及使用Lua脚本来保证原子性。还讨论了为何不能使用Python脚本,并提出了处理锁过期时间的策略,如设置冗余过期时间和使用守护线程进行续期。最后,推荐了Python中的redis-lock库用于简化分布式锁的管理。
摘要由CSDN通过智能技术生成

怎样实现redis分布式锁?

1 简单分布式锁

1 加锁:SET $lock_key $unique_id EX $expire_time NX
2 操作共享资源
3 释放锁:先 GET 判断锁是否归属自己,再 DEL 释放锁。Lua 脚本(或使用redis事务)保证两步操作的原子性。
使用lua脚本:

if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end
import redis

def release_lock(redis_conn, lock_key, lock_value):
    # 定义释放锁的 Lua 脚本
    release_lock_script = """
    if redis.call("get", KEYS[1]) == ARGV[1] then
        return redis.call("del", KEYS[1])
    else
        return 0
    end
    """
    # 执行 Lua 脚本
    result = redis_conn.eval(release_lock_script, 1, lock_key, lock_value)
    return result == 1

使用redis事务:

import redis

def release_lock(redis_conn, lock_key, lock_value):
    # 开启 Redis 事务
    with redis_conn.pipeline() as pipe:
        while True:
            try:
                # 监视锁的键,确保在事务执行期间锁没有被修改
                pipe.watch(lock_key)
                
                # 获取当前锁的值
                current_value = pipe.get(lock_key)
                
                # 检查锁是否存在且与当前客户端持有的锁值相同
                if current_value and current_value.decode("utf-8") == lock_value:
                    # 在事务中删除锁
                    pipe.multi()
                    pipe.delete(lock_key)
                    pipe.execute()
                    return True
                else:
                    # 锁已经被其他客户端修改,取消事务
                    pipe.unwatch()
                    break
            except redis.WatchError:
                # 如果其他客户端修改了锁,重试
                continue
    return False

[1]为什么使用lua脚本可以保证释放锁时的原子性?
Lua 脚本在 Redis的执行是原子性的,这主要是由于 Redis 本身的单线程执行模型以及 Lua 脚本的执行机制保证的。在 Redis 中,每个客户端的命令都是由 Redis 服务器单线程依次执行的,这确保了多个命令的原子性执行。

当执行 Lua 脚本时,Redis 会将 Lua 脚本作为整体进行执行,不允许其被其他客户端的操作打断。这样可以保证 Lua 脚本在执行时不会被其他操作打断,从而保证了 Lua 脚本的原子性。

此外,Redis 在执行 Lua 脚本时,使用了 EVAL 命令将 Lua 脚本传递给 Redis 服务器,然后 Redis 服务器会将 Lua 脚本编译为字节码并缓存起来,这样可以提高 Lua 脚本的执行效率,同时也可以保证 Lua 脚本执行的原子性。

[2]为什么可以使用lua脚本,不能使用python脚本?
Python 是一种多线程语言,而 Lua 是单线程语言。在 Python 中,虽然有多线程支持,但由于全局解释器锁(GIL)的存在,多个线程并不能真正并行执行 Python 代码,因此在多线程环境下,Python 会存在线程切换的情况。

而 Lua 是单线程语言,不具备多线程支持。在执行 Lua 脚本时,只有一个 Lua 解释器负责解析和执行脚本,因此不会存在多个线程之间的切换。这意味着在执行 Lua 脚本时,整个过程都是顺序执行的,不会被中断或切换到其他线程执行。

所以,你可以理解为 Python 在多线程环境下可能存在线程切换,而 Lua 在执行脚本时是单线程的,不存在线程切换。这也是 Lua 脚本执行原子性的一个重要原因。

4 锁过期时间不好评估怎么办?
锁的过期时间如果评估不好,这个锁就会有「提前」过期的风险。
妥协方案是,尽量「冗余」过期时间,降低锁提前过期的概率。这个方案其实也不能完美解决问题,那怎么办呢?
可以设计这样的方案:加锁时,先设置一个过期时间,然后我们开启一个「守护线程」,定时去检测这个锁的失效时间,如果锁快要过期了,操作共享资源还未完成,那么就自动对锁进行「续期」,重新设置过期时间。

在python中有封装好的redis-lock库

python
import time
from redis import Redis
from redis_lock import Lock

# 连接 Redis
redis = Redis(host='localhost', port=6379, db=0)

# 创建一个锁
lock = Lock(redis, 'my_lock')

# 获取锁
with lock:
    print("获得锁,执行任务...")
    time.sleep(5)  # 模拟执行任务的过程
    print("任务执行完成")

# 锁自动释放后,执行其他操作
print("锁已释放,执行其他操作...")

在上面的示例中,我们首先创建了一个 Redis 连接对象 redis,然后创建了一个名为 my_lock 的锁对象 lock。接着,我们使用 with 语句来获取锁,并在获取锁成功后执行任务。在任务执行完成后,锁会自动释放,然后执行其他操作。

使用 redis-lock 可以很方便地实现分布式系统中的锁机制,确保在多个客户端之间对共享资源的安全访问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值