分布式锁实战:Redis与ZooKeeper对比选型与实现方案

分布式锁实战:Redis与ZooKeeper对比选型与实现方案

大家好,我是迪哥。分布式锁是分布式系统中最基础也是最重要的组件之一,从 Redis Redlock 到 ZooKeeper 临时节点,从数据库乐观锁到 etcd,我们尝试过多种方案。今天就聊聊分布式锁的选型和实现经验。

分布式锁需求分析

核心特性

特性 说明
互斥性 同一时刻只能有一个客户端持有锁
超时释放 防止死锁
可重入 同一客户端可重复获取同一把锁
高可用 锁服务本身要高可用
公平性 按请求顺序获取锁

Redis 分布式锁

基础实现

java 复制代码
@Service
public class RedisLockService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String LOCK_PREFIX = "lock:";
    private static final long DEFAULT_EXPIRE = 30000; // 30秒
    
    public boolean tryLock(String lockKey, String requestId, long expire) {
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(LOCK_PREFIX + lockKey, requestId, expire, TimeUnit.MILLISECONDS);
        return Boolean.TRUE.equals(result);
    }
    
    public void unlock(String lockKey, String requestId) {
        String script = """
            if redis.call('get', KEYS[1]) == ARGV[1] then
                return redis.call('del', KEYS[1])
            else
                return 0
            end
            """;
        redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
            Collections.singletonList(LOCK_PREFIX + lockKey), requestId);
    }
}

Redlock 算法

java 复制代码
public class RedlockService {
    private final List<StringRedisTemplate> redisTemplates;
    
    public boolean acquireLock(String lockKey, String requestId, long expire) {
        int quorum = redisTemplates.size() / 2 + 1;
        int successCount = 0;
        
        for (StringRedisTemplate redis : redisTemplates) {
            try {
                Boolean result = redis.opsForValue()
                    .setIfAbsent(lockKey, requestId, expire, TimeUnit.MILLISECONDS);
                if (Boolean.TRUE.equals(result)) {
                    successCount++;
                }
            } catch (Exception e) {
                // 节点不可用
            }
        }
        
        return successCount >= quorum;
    }
}

Redis 锁的问题

  1. 单点故障:主从切换时可能丢失锁
  2. 超时问题:业务执行时间超过锁过期时间
  3. 续期问题:需要看门狗机制

ZooKeeper 分布式锁

基于临时节点的实现

java 复制代码
public class ZkLockService {
    private final CuratorFramework client;
    
    public boolean tryLock(String lockKey) throws Exception {
        String lockPath = "/locks/" + lockKey;
        
        // 创建临时有序节点
        String nodePath = client.create()
            .creatingParentContainersIfNeeded()
            .withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
            .forPath(lockPath + "/lock-");
        
        // 获取所有子节点
        List<String> children = client.getChildren().forPath(lockPath);
        Collections.sort(children);
        
        // 判断是否是最小节点
        if (nodePath.endsWith(children.get(0))) {
            return true;
        }
        
        // 监听前一个节点
        String prevNode = children.get(findPrevIndex(children, nodePath));
        CountDownLatch latch = new CountDownLatch(1);
        Watcher watcher = event -> {
            if (event.getType() == Watcher.Event.EventType.NodeDeleted) {
                latch.countDown();
            }
        };
        
        client.getData().usingWatcher(watcher).forPath(lockPath + "/" + prevNode);
        latch.await();
        
        // 重新检查
        return checkMinNode(nodePath, lockPath);
    }
}

ZooKeeper 锁的优势

  1. 天生分布式:基于 ZAB 协议,天然高可用
  2. 自动释放:会话断开自动删除临时节点
  3. 公平锁:按节点顺序获取锁

对比选型

维度 Redis ZooKeeper
性能 高(百万级 QPS) 中等(万级 QPS)
可靠性 依赖主从复制 基于 ZAB,更可靠
实现复杂度 简单 复杂
锁类型 可实现公平/非公平 天然公平锁
适用场景 高吞吐、非强一致场景 强一致、低吞吐场景

数据库分布式锁

乐观锁

java 复制代码
@Service
public class OptimisticLockService {
    @Autowired
    private OrderMapper orderMapper;
    
    @Transactional
    public boolean updateOrder(Long orderId, Integer expectedVersion) {
        int rows = orderMapper.updateWithVersion(orderId, expectedVersion);
        return rows > 0;
    }
}
sql 复制代码
UPDATE orders 
SET status = 'PAID', version = version + 1 
WHERE id = ? AND version = ?;

悲观锁

java 复制代码
@Transactional
public void processOrder(Long orderId) {
    Order order = orderMapper.selectForUpdate(orderId);
    // 处理业务
    orderMapper.update(order);
}

最佳实践

选择建议

复制代码
              ┌──────────────────┐
              │   性能要求高?    │
              └────────┬─────────┘
                       │
              ┌────────┴────────┐
              ▼                 ▼
           Yes                 No
              │                 │
     ┌────────▼────────┐  ┌─────▼─────┐
     │   使用 Redis    │  │ZooKeeper │
     │  Redlock 模式   │  │   或      │
     └─────────────────┘  │  数据库锁  │
                          └───────────┘

注意事项

  1. 锁粒度:尽量细粒度,避免锁住整个资源
  2. 超时设置:合理设置过期时间,考虑业务执行时间
  3. 异常处理:获取锁失败时的降级策略
  4. 监控告警:监控锁的获取成功率、等待时间

说到分布式锁,我家那只叫 Docker 的哈士奇最近学会了"锁机制"------它会把喜欢的玩具咬在嘴里,谁要都不给,这独占性比我们的 Redis 锁还强 😂

我是迪哥,我们下期再见!

相关推荐
Refrain_zc4 小时前
Android 应用内的APK 安装(可复制)
java
杨运交4 小时前
[020][缓存模块]基于 BeanCreator 的缓存管理器创建器模式设计与实践
java·spring·缓存
risc1234564 小时前
DocumentsWriterDeleteQueue 的核心设计思想
java·全文检索·lucene
风味蘑菇干5 小时前
Stream基础题目
java·算法
2501_932750265 小时前
Java反射机制基础入门
java·开发语言
500845 小时前
HCCL 集合通信编程:多卡协同的正确姿势
java·flutter·性能优化·electron·wpf
asdfg12589635 小时前
Java中的Comparator 和JS中的回调函数好相似
java·开发语言
会编程的土豆5 小时前
消息队列(MQ)入门笔记
java·笔记·spring
专注VB编程开发20年5 小时前
python运行提速方案全解
java·linux·服务器