Python+Redis从零打造分布式锁实战示例

 更新时间:2024年01月22日 08:51:07   作者:傻子的尴尬 IT智慧谷  
Redis作为一款高性能的内存键值数据库,凭借其支持原子操作、高并发和数据持久化等特性,非常适合用来实现分布式锁,本文将详细探讨如何使用Python结合Redis从简单到复杂地实现分布式锁,并提供相应的示例代码

引言

在分布式系统中,多个节点可能需要访问同一共享资源,这就需要一种协调机制来保证在同一时刻只有一个节点进行操作,这就是分布式锁的作用。

1. 简单实现(基于SETNX命令)

使用setnx命令(set if not exists)尝试设置一个key-value对,如果key不存在,则设置成功并返回1,表示获取到了锁。

import redis
import time
def acquire_lock(redis_client, lock_key):
    identifier = str(time.time()) + str(id(threading.current_thread()))
    acquired = redis_client.setnx(lock_key, identifier)
    return acquired
def release_lock(redis_client, lock_key, identifier):
    pipe = redis_client.pipeline(True)
    while True:
        try:
            pipe.watch(lock_key)
            if pipe.get(lock_key) == identifier:
                pipe.multi()
                pipe.delete(lock_key)
                pipe.execute()
                return True
            pipe.unwatch()
            break
        except redis.exceptions.WatchError:
            pass
    return False
# 使用示例
r = redis.Redis(host='localhost', port=6379, db=0)
lock_key = 'mylock'
if acquire_lock(r, lock_key):
    print("Lock acquired")
    # 执行临界区代码...
    release_lock(r, lock_key, identifier)

2. 改进:设置超时时间(expire命令)

为防止进程崩溃导致锁无法释放,可以给锁设置一个过期时间。

def acquire_lock_with_timeout(redis_client, lock_key, expire_time):
    identifier = str(time.time()) + str(id(threading.current_thread()))
    while True:
        result = redis_client.set(lock_key, identifier, nx=True, ex=expire_time)
        if result:
            return identifier
        time.sleep(0.1)
# 使用示例不变,但在acquire_lock_with_timeout函数中增加了expire_time参数

3. 进一步改进:使用Lua脚本保证原子性

在释放锁的时候,需要确保删除的是自己设置的锁,且这个操作是原子的。可以使用Lua脚本来实现。

RELEASE_LOCK_SCRIPT = """
if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end
"""
def release_lock LUA(redis_client, lock_key, identifier):
    release_script = redis_client.register_script(RELEASE_LOCK_SCRIPT)
    released = release_script(keys=[lock_key], args=[identifier])
    return bool(released)
# 使用示例不变

4. 更完善的实现,Redlock算法

Redlock算法的实现相对复杂,以下是一个简化的Python示例,展示了如何在多个Redis实例上尝试获取和释放锁。请注意,在实际应用中,需要确保Redis实例之间的时间同步,并且考虑网络延迟等因素。

import redis
import time
class RedLock:
    def __init__(self, masters, ttl):
        self.masters = [redis.Redis(host=host, port=port, db=db) for host, port, db in masters]
        self.quorum = len(self.masters) // 2 + 1
        self.ttl = ttl
    def lock(self, resource_id):
        expiration = time.time() + self.ttl
        while time.time() < expiration:
            acquired_count = 0
            locked_instances = []
            # 尝试在每个Redis实例上获取锁
            for master in self.masters:
                if master.set(resource_id, 'locked', nx=True, px=self.ttl):
                    acquired_count += 1
                    locked_instances.append(master)
            # 如果获得超过半数以上的锁,则认为成功获取分布式锁
            if acquired_count > self.quorum:
                return locked_instances
            # 清除已获取但未达到法定数量的锁
            for master in locked_instances:
                master.delete(resource_id)
            # 等待一段时间后重试
            time.sleep(0.1)
        raise Exception("Could not acquire lock")
    def unlock(self, resource_id, masters_with_lock):
        for master in masters_with_lock:
            # 使用Lua脚本保证原子性地删除锁(与前面简单实现中的释放锁方法类似)
            RELEASE_LOCK_SCRIPT = """
            if redis.call("get", KEYS[1]) == "locked" then
                return redis.call("del", KEYS[1])
            else
                return 0
            end
            """
            release_script = master.register_script(RELEASE_LOCK_SCRIPT)
            release_script(keys=[resource_id])
# 示例用法:
masters = [('localhost', 6379, 0), ('localhost', 6380, 0)]  # 假设有两个Redis实例
redlock = RedLock(masters, 10000)  # 设置锁的有效期为10秒
resource_id = 'my_resource'
try:
    masters_with_lock = redlock.lock(resource_id)
    print(f"Acquired Redlock on resource {resource_id}")
    # 执行临界区代码...
finally:
    redlock.unlock(resource_id, masters_with_lock)

此示例仅为了说明Redlock算法的核心思想,并未涵盖所有边界条件和异常处理,如Redis实例宕机、网络延迟导致时间不一致等情况,请在生产环境中使用时结合实际情况进行优化和完善。

总结

通过以上介绍和示例代码,我们了解了如何基于Python与Redis实现不同层次复杂度的分布式锁。从简单的SETNX命令获取锁,到设置锁的超时时间以防止进程崩溃导致死锁,再到利用Lua脚本保证释放锁操作的原子性,以及针对大规模分布式环境考虑采用Redlock算法提高系统的容错性和安全性。这些方法可以根据实际业务场景选择合适的方式来实现分布式锁,从而有效解决多节点环境下对共享资源的竞争问题。随着技术的发展和需求的变化,分布式锁的实现方式也将持续演进和完善,为构建更稳定、高效的分布式系统提供有力支撑。

以上就是Python+Redis从零打造分布式锁实战示例的详细内容,更多关于Python Redis分布式锁的资料请关注脚本之家其它相关文章!

相关文章

  • 9个提高 Python 编程的小技巧

    9个提高 Python 编程的小技巧

    这篇文章主要介绍了9个提高 Python 编程的小技巧,下文分享python编程技巧,需要的小伙伴可以参考一下,希望对你的学习有所帮助
    2022-05-05
  • python实现简单银行管理系统

    python实现简单银行管理系统

    这篇文章主要为大家详细介绍了python实现简单银行管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • 教你用Django将前端的数据存入Mysql数据库

    教你用Django将前端的数据存入Mysql数据库

    这篇文章主要给大家介绍了关于如何用Django将前端的数据存入Mysql数据库的相关资料,文中通过图文以及示例代码介绍的非常详细,对大家学习或者使用Django具有一定的参考学习价值,需要的朋友可以参考下
    2021-11-11
  • python plotly设置go.Scatter为实线实例

    python plotly设置go.Scatter为实线实例

    这篇文章主要为大家介绍了python plotly设置go.Scatter为实线线条的样式实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Python深度学习pytorch神经网络Dropout应用详解解

    Python深度学习pytorch神经网络Dropout应用详解解

    这篇文章主要为大家介绍了Python深度学习中关于pytorch神经网络Dropout的应用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2021-10-10
  • Python打包编译工具Pyinstaller与Nuitka特性超详细对比

    Python打包编译工具Pyinstaller与Nuitka特性超详细对比

    应用程序开发完毕,一般都需要对程序进行打包后发布,除开源项目外,很少有发布源码的,这篇文章主要介绍了Python打包编译工具Pyinstaller与Nuitka特性对比的相关资料,需要的朋友可以参考下
    2026-03-03
  • Django之第三方平台QQ授权登录的实现

    Django之第三方平台QQ授权登录的实现

    本文主要介绍了Django之第三方平台QQ授权登录的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • Python安装selenium包详细过程

    Python安装selenium包详细过程

    在本篇文章里小编给大家整理了关于Python安装selenium包详细过程,需要的朋友们可以学习下。
    2019-07-07
  • Python中asyncio的多种用法举例(异步同步)

    Python中asyncio的多种用法举例(异步同步)

    这篇文章主要给大家介绍了关于Python中asyncio的多种用法,包括顺序执行非异步任务、顺序执行异步任务、并行执行异步任务以及并行执行非异步任务,通过使用asyncio模块,可以有效地提高程序的执行效率,尤其是在处理I/O密集型任务时,需要的朋友可以参考下
    2024-11-11
  • Python数据清洗之利用pandas筛选数据详解

    Python数据清洗之利用pandas筛选数据详解

    这篇文章主要介绍了Python数据清洗之利用pandas筛选数据详解,Pandas是一个用于数据分析和处理的Python库,它提供了高效的数据结构和数据分析工具,使得数据的清洗、转换、分析和可视化变得更加容易和灵活,需要的朋友可以参考下
    2023-08-08

最新评论