跳转到内容

分布式锁

来自代码酷
Admin留言 | 贡献2025年5月12日 (一) 00:24的版本 (Page creation by admin bot)

(差异) ←上一版本 | 已核准修订 (差异) | 最后版本 (差异) | 下一版本→ (差异)

分布式锁[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

分布式锁是一种在分布式系统中协调多个进程或服务对共享资源进行互斥访问的机制。在单机环境中,可以使用线程锁(如Java的`synchronized`或`ReentrantLock`)实现同步,但在分布式环境下,由于进程分布在不同的物理节点上,需要一种跨节点的锁机制来保证数据一致性。

分布式锁的核心特性包括:

  • 互斥性:同一时刻只有一个客户端能持有锁。
  • 可重入性:同一个客户端可以多次获取同一把锁。
  • 锁超时:避免死锁,锁需支持自动释放。
  • 高可用:锁服务需具备容错能力,即使部分节点故障仍能工作。

实现方式[编辑 | 编辑源代码]

基于数据库[编辑 | 编辑源代码]

通过数据库的唯一索引或乐观锁实现,例如创建一张锁表:

CREATE TABLE distributed_lock (
    lock_name VARCHAR(64) PRIMARY KEY,
    owner_id VARCHAR(255),
    expire_time TIMESTAMP
);

获取锁时执行:

INSERT INTO distributed_lock VALUES ('order_lock', 'client1', NOW() + INTERVAL 30 SECOND);
-- 失败则说明锁已被占用

缺点:数据库性能瓶颈,不具备自动过期能力。

基于Redis[编辑 | 编辑源代码]

使用Redis的`SETNX`(SET if Not eXists)命令:

import redis
r = redis.Redis()

# 获取锁(设置过期时间防止死锁)
lock_acquired = r.set("order_lock", "client1", nx=True, ex=30)
if lock_acquired:
    try:
        # 执行业务逻辑
    finally:
        r.delete("order_lock")  # 释放锁

关键点

  • 必须设置过期时间(`ex`参数)
  • 释放锁时需验证持有者(防止误删其他客户端的锁)

基于ZooKeeper[编辑 | 编辑源代码]

利用ZooKeeper的临时有序节点:

sequenceDiagram Client->>ZooKeeper: 创建临时节点/lock/order-0001 ZooKeeper-->>Client: 节点创建成功 Client->>ZooKeeper: 检查自己是否是最小序号节点 ZooKeeper-->>Client: 确认是最小节点(获得锁) Client->>ZooKeeper: 业务完成后删除节点

Java示例(使用Curator框架):

InterProcessMutex lock = new InterProcessMutex(client, "/order_lock");
if (lock.acquire(30, TimeUnit.SECONDS)) {
    try {
        // 访问共享资源
    } finally {
        lock.release();
    }
}

算法原理[编辑 | 编辑源代码]

RedLock算法[编辑 | 编辑源代码]

Redis官方推荐的分布式锁算法,需在多个独立Redis实例上获取锁: N=2F+1 其中N是总节点数,F是允许的故障节点数。客户端需要在多数节点(>N/2)上获得锁才算成功。

实现步骤: 1. 获取当前时间T1 2. 依次向N个Redis实例请求锁 3. 计算获取锁耗时T2-T1,如果小于锁超时时间且成功获取多数锁,则成功 4. 实际锁有效时间 = 初始设置时间 - (T2-T1)

实际案例[编辑 | 编辑源代码]

电商库存扣减[编辑 | 编辑源代码]

场景:秒杀活动中防止超卖 1. 用户下单时尝试获取商品ID对应的分布式锁 2. 查询库存并扣减 3. 释放锁

分布式任务调度[编辑 | 编辑源代码]

场景:确保定时任务只在集群中的一个节点执行 1. 任务触发时各节点竞争锁 2. 获得锁的节点执行任务 3. 其他节点跳过执行

常见问题[编辑 | 编辑源代码]

  • 时钟漂移问题:不同机器时钟不一致可能导致锁过早失效。解决方案:使用NTP同步时钟,或采用无需绝对时间的算法(如ZooKeeper版本号)
  • 长时间阻塞:持有锁的客户端崩溃可能导致资源长时间不可用。解决方案:设置合理的超时时间,实现锁续约机制(如Redisson的WatchDog)
  • 脑裂问题:网络分区导致多个客户端同时持有锁。解决方案:使用多数派算法(如RedLock)

进阶优化[编辑 | 编辑源代码]

  • 锁分段:将单个热点锁拆分为多个子锁(如ConcurrentHashMap的分段锁思想),提高并发度
  • 读写锁:区分读锁(共享)和写锁(排他),适用于读多写少场景
  • 乐观锁:通过版本号或CAS机制实现无锁编程,如:
UPDATE inventory SET stock = stock - 1 WHERE item_id = 100 AND stock >= 1;
-- 检查affected_rows是否大于0

总结[编辑 | 编辑源代码]

分布式锁是分布式系统的基础组件,选择实现方案时需要权衡:

方案 优点 缺点
数据库 实现简单,无需额外组件 性能差,无故障转移
Redis 高性能,支持丰富特性 需处理复杂边界条件
ZooKeeper 可靠性高,原生支持临时节点 性能较低,依赖ZK集群

实际开发中推荐使用成熟的开源实现(如Redisson、Curator)而非自行造轮子,以规避潜在的分布式系统陷阱。