当前位置: 首页 > news >正文

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

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

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

分布式锁需求分析

核心特性

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

Redis 分布式锁

基础实现

@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 算法

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 分布式锁

基于临时节点的实现

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. 公平锁:按节点顺序获取锁

对比选型

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

数据库分布式锁

乐观锁

@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; } }
UPDATE orders SET status = 'PAID', version = version + 1 WHERE id = ? AND version = ?;

悲观锁

@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 锁还强 😂

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

http://www.jsqmd.com/news/800372/

相关文章:

  • 别再只用NDVI了!在GEE里用CODED算法,结合土壤湿度等多特征检测植被缓慢退化
  • 【Perplexity×Google Scholar整合实战指南】:20年科研工具专家亲授3步打通AI搜索与学术文献闭环
  • 如何高效解密华为光猫配置文件:终极操作指南
  • ComfyClaw:用Python代码自动化操控ComfyUI工作流
  • 面向密集预测任务的神经架构搜索:原理、挑战与实战指南
  • AI智能体七日实战:从设计到部署的自动化专家系统构建
  • AI代理治理零风险上线:asqav观察模式与渐进式集成实践
  • GLB纹理提取利器:glb_texture_extractor工具详解与实战
  • 生成式AI在医学影像中的应用:从原理到临床落地的深度解析
  • 3分钟搞定Mac NTFS读写:Nigate开源工具让跨平台文件传输不再烦恼
  • 告别SQL*Plus:用PLSQL Developer 13提升Oracle开发效率的5个实战技巧
  • Godot开发实战:高效利用开源代码库提升游戏开发效率
  • Matlab流程控制实战:掌握switch-case-otherwise的精准条件分支
  • 基于大语言模型的自动化数据标注:Autolabel实战指南
  • AI营销技能库:模块化设计提升Claude Code与智能体工作流效率
  • ST-Transformer在海洋缺氧预测中的性能评估与架构对比
  • AI智能体评估困境:从静态指标到动态能力成长评估的范式转变
  • LLM推理中的内存卸载技术优化与实践
  • 基于WebGL与Three.js的宇宙模拟器:从N体问题到实时渲染
  • 硬件工程师必读:九大核心算法如何重塑芯片与系统设计
  • 克鲁斯卡尔(Kruskal) vs 普里姆(Prim):图解对比两大最小生成树算法,看完就知道项目里该用哪个
  • 别再只会用Matplotlib画基础热力图了!这5个高级定制技巧让你的图表更专业
  • 从仿真到PCB:基于74LS系列芯片的十字路口交通灯系统实战设计
  • 自动驾驶安全迷思:从94%人为错误统计到ADAS与系统安全工程实践
  • YOLO11手语识别实战:高精度关键点检测与端到端优化
  • ConcurrentHashMap详细讲解(java)
  • 中国半导体设计产业:从制造到创新的演进逻辑与未来挑战
  • SAM基础模型:零样本图像分割的原理与工业实践
  • LM Studio Python SDK 深度解析:本地大语言模型编程接口实战指南
  • 计算机视觉与3D重建:模型加速与质量优化的全栈实践