分布式锁实现实战:Redis与etcd与Zookeeper方案对比

分布式锁是多节点部署场景下保证共享资源互斥访问的核心机制。库存扣减、订单防重、定时任务去重等业务场景需要可靠的分布式锁。Redis、etcd、Zookeeper三种方案在实现原理、一致性保证、性能特性上各有侧重,选型时需要根据业务对一致性和吞吐量的要求权衡决策。

分布式锁核心要求与Redis实现

分布式锁需满足四个基本要求:互斥性(同一时刻仅一个客户端持有锁)、避免死锁(锁有超时时间,持有者崩机后自动释放)、可重入(同一客户端可多次获取)、高可用(锁服务自身不能成为单点故障)。

Redis单节点分布式锁使用SET NX PX命令实现原子加锁:

import redis, uuid, time

class RedisLock:
    def __init__(self, redis_client, lock_key, expire_ms=10000):
        self.redis = redis_client
        self.lock_key = lock_key
        self.expire_ms = expire_ms
        self.lock_value = str(uuid.uuid4())
    
    def acquire(self, retry_count=3, retry_delay=0.2):
        for i in range(retry_count):
            result = self.redis.set(
                self.lock_key, self.lock_value,
                nx=True, px=self.expire_ms
            )
            if result:
                return True
            time.sleep(retry_delay)
        return False
    
    def release(self):
        lua_script = """
        if redis.call('get', KEYS[1]) == ARGV[1] then
            return redis.call('del', KEYS[1])
        else
            return 0
        end
        """
        self.redis.eval(lua_script, 1, self.lock_key, self.lock_value)

可重入锁实现,使用Hash结构记录重入次数:

class ReentrantRedisLock:
    def acquire(self):
        lua_acquire = """
        if redis.call('exists', KEYS[1]) == 0 then
            redis.call('hset', KEYS[1], ARGV[1], 1)
            redis.call('pexpire', KEYS[1], ARGV[2])
            return 1
        end
        if redis.call('hexists', KEYS[1], ARGV[1]) == 1 then
            redis.call('hincrby', KEYS[1], ARGV[1], 1)
            redis.call('pexpire', KEYS[1], ARGV[2])
            return 1
        end
        return 0
        """
        return self.redis.eval(lua_acquire, 1, self.lock_key, 
                              self.client_id, self.expire_ms)

RedLock算法多节点加锁方案

单Redis实例存在主从切换丢锁风险。RedLock算法在多个独立Redis节点上同时加锁,多数节点加锁成功即视为锁获取成功:

class RedLock:
    def __init__(self, redis_nodes, lock_key, expire_ms=10000):
        self.nodes = redis_nodes
        self.lock_key = lock_key
        self.expire_ms = expire_ms
        self.lock_value = str(uuid.uuid4())
        self.quorum = len(redis_nodes) // 2 + 1
    
    def acquire(self):
        start_time = time.time()
        success_count = 0
        for node in self.nodes:
            try:
                result = node.set(self.lock_key, self.lock_value,
                                 nx=True, px=self.expire_ms)
                if result:
                    success_count += 1
            except Exception:
                pass
        elapsed_ms = (time.time() - start_time) * 1000
        if success_count >= self.quorum and elapsed_ms < self.expire_ms:
            return True
        else:
            self.release()
            return False

etcd分布式锁实现

etcd基于Raft一致性算法,提供强一致的租约机制。使用Lease + Compare-And-Swap实现分布式锁:

import etcd3, time, uuid

class EtcdLock:
    def __init__(self, endpoints, lock_key, lease_ttl=10):
        self.client = etcd3.client(host=endpoints)
        self.lock_key = lock_key
        self.lease_ttl = lease_ttl
        self.lease = None
        self.lock_value = str(uuid.uuid4())
    
    def acquire(self, timeout=10):
        start = time.time()
        while time.time() - start < timeout:
            self.lease = self.client.lease(self.lease_ttl)
            status, _ = self.client.transaction(
                compare=[self.client.transactions.create_revision(self.lock_key) == 0],
                success=[self.client.transactions.put(self.lock_key, self.lock_value, lease=self.lease.id)],
                failure=[]
            )
            if status:
                return True
            self.lease.revoke()
            time.sleep(0.1)
        return False
    
    def release(self):
        if self.lease:
            self.lease.revoke()
            self.lease = None

三种方案性能与一致性对比

维度 Redis单节点 RedLock etcd Zookeeper
一致性 AP(异步复制) CP(多数派) CP(Raft) CP(ZAB)
性能 10万+ QPS 1万+ QPS 数千QPS 数千QPS
锁续期 需额外实现 需额外实现 Lease自动续期 Session机制
复杂度
故障恢复 主从切换可能丢锁 多数节点存活即可 Raft自动选主 Leader选举

选型决策:对性能要求高且能接受极端情况下少量不一致的场景(如缓存预热、限流计数)用Redis单节点。对一致性要求严格的金融场景(如余额扣减、库存锁定)用etcd,Lease机制天然支持锁自动释放和续期。Zookeeper适合已有ZK集群的Dubbo/Kafka等中间件生态。RedLock在理论上有争议(时钟漂移问题),实际生产中使用不如etcd普遍。

分布式锁使用中的注意事项:锁超时时间应大于业务执行时间但不宜过长,避免持有者崩溃后资源长时间被锁。加锁后业务执行需做幂等处理,防止锁自动释放后被其他客户端获取导致重复执行。高并发场景下获取锁失败时应使用指数退避重试,避免大量客户端同时重试造成Redis压力。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/fen-bu-shi-suo-shi-xian-shi-zhan-redis-yu-etcd-yu-zookeeper/

(0)
小编小编
上一篇 52分钟前
下一篇 51分钟前

相关推荐