Python中分布式互斥锁是什么?

看了一些 PYTHON 中关于如何实现锁机制的文章,但是对于什么是分布式互斥锁这个基本的概念还是不明白,不知有没有合适的说明材料,能否推荐一下,以及在 PYTHON 中如何实现的说明,万分感谢了!
Python中分布式互斥锁是什么?

7 回复

分布式互斥锁(Distributed Mutex Lock)是在分布式系统中,用于协调多个独立进程或节点,确保它们在同一时刻只有一个能访问共享资源或执行特定代码段的机制。这解决了在单机多线程环境中的锁(如threading.Lock)无法跨网络、跨机器工作的问题。

核心原理是引入一个所有节点都能访问的、具备强一致性的中央协调服务(如 ZooKeeper、etcd、Redis)。节点通过在这个服务上竞争创建一个特定的“键”或“节点”来获取锁。因为协调服务保证了操作的原子性和顺序性,所以能实现全局唯一的互斥。

下面是一个使用 redis 库实现分布式锁的经典示例(非生产级,用于演示原理):

import redis
import time
import uuid

class DistributedLock:
    def __init__(self, redis_client, lock_name, expire_time=30):
        self.redis = redis_client
        self.lock_name = f"distributed_lock:{lock_name}"
        self.expire_time = expire_time
        self.identifier = str(uuid.uuid4()) # 唯一标识当前锁的持有者

    def acquire(self, timeout=10):
        """尝试获取锁,timeout为获取超时时间(秒)"""
        end = time.time() + timeout
        while time.time() < end:
            # 关键操作:使用SET命令,NX参数确保仅当键不存在时设置,EX参数设置过期时间
            # 值设置为唯一标识符,用于安全释放
            if self.redis.set(self.lock_name, self.identifier, nx=True, ex=self.expire_time):
                return True # 获取成功
            time.sleep(0.1) # 短暂等待后重试
        return False # 超时,获取失败

    def release(self):
        """释放锁。使用Lua脚本保证原子性:只有锁的持有者才能删除"""
        lua_script = """
        if redis.call("get", KEYS[1]) == ARGV[1] then
            return redis.call("del", KEYS[1])
        else
            return 0
        end
        """
        # 执行Lua脚本,确保检查标识符和删除是原子操作
        release_script = self.redis.register_script(lua_script)
        result = release_script(keys=[self.lock_name], args=[self.identifier])
        return result == 1

# 使用示例
if __name__ == "__main__":
    # 连接到Redis服务器(所有竞争锁的进程需连接同一实例/集群)
    r = redis.Redis(host='localhost', port=6379, db=0)

    lock = DistributedLock(r, "my_shared_resource")

    if lock.acquire():
        try:
            print("成功获取分布式锁,开始操作共享资源...")
            # 在这里执行需要互斥访问的临界区代码
            time.sleep(5) # 模拟耗时操作
        finally:
            # 确保最终释放锁
            if lock.release():
                print("锁已释放")
            else:
                print("释放失败(可能已超时自动过期,或不是锁的持有者)")
    else:
        print("获取锁超时,可能其他节点正持有锁")

关键点解释:

  1. 唯一标识符 (identifier): 防止误删。只有自己设置的锁才能自己释放。
  2. 原子性获取: set 命令配合 nx (Not eXists) 和 ex (过期时间) 参数,一步完成“检查-设置-设过期”的操作。
  3. 原子性释放: 使用 Lua 脚本,将“检查标识符”和“删除键”作为一个原子命令执行,避免在检查后、删除前锁过期并被其他进程获取导致的误删。
  4. 过期时间 (expire_time): 必须设置,作为安全机制。防止持有锁的进程崩溃后,锁永远不被释放(死锁)。

一句话总结:用中央存储(如Redis)的一个键作为信号量,通过原子操作竞争设置来实现跨进程互斥。 对于生产环境,建议直接使用更成熟的库,如 redlock-py(实现RedLock算法)或 etcd3kazoo(ZooKeeper客户端)提供的分布式锁原语。

如果所谓的分布式锁是指一个 Lock 对象可以在不同进程甚至不同机器上使用,并且性能要求和规模不是很高的话,可以子类继承自 multiprocessing.managers.BaseManager,使用 TCP 通信,register 一个 threading.Lock 实例。具体看 multiprocessing 的文档。

有好几种不同的类型
google distributed mutual exclusion algorithms

简单的,数据库行锁,缓存 set not exist
复杂的看一下 chubby,redlock

行锁、set not exist 本身并不是分布式锁:锁完全由数据库服务器管理

首先应该了解单进程内多线程锁的机制,主要是通过 CPU 和操作系统配合来实现资源分配和保护。然后考虑多进程之间如何实现资源保护,原理上讲都是一样的,也需要个中控的系统来调配。结合 Redis 的分布式锁理解下吧。

回到顶部