RedisvsZooKeeper:分布式锁的“抢车位”大战,谁才是老司机?

南春编程 2025-02-21 05:52:11
深夜停车场的锁争夺战

凌晨2点,某小区停车场发生诡异一幕:两辆特斯拉同时扫描到最后一个车位,系统显示"正在分配中",结果两车头对头卡在通道整整10分钟…这像极了分布式系统中的锁竞争!

今天我们就用"抢车位"和"医院叫号"的比喻,手把手教你用Redis和ZooKeeper设计分布式锁。

Redis篇:地锁式抢车位(简单粗暴版)场景还原:

想象你在停车场看到空位,立刻降下地锁(SETNX命令):

# Python伪代码演示if redis.set("车位A", "京A88888", nx=True, ex=30): print("抢锁成功!30分钟内停好车") else: print("手速太慢,车位被抢了!")

致命漏洞三连击:

地锁卡死风险:如果设置过期时间前服务器宕机(好比地锁降下但没设置倒计时),车位永远被占钥匙被偷风险:其他车用假钥匙(随机值不匹配)也能升起地锁看门狗罢工:业务处理超过30分钟,地锁自动升起导致车辆被拖走(锁过期)进阶方案:红锁算法(RedLock)

就像在5个停车场同时抢车位:

记录抢锁开始时间T1(精确到毫秒)向5个停车场发起抢锁请求当3个停车场返回成功时,计算总耗时T2-T1若总耗时 < 锁有效期(如30秒),则抢锁成功

真实翻车案例:某电商大促时用Redis单节点锁,结果主从切换导致超卖1000件商品,技术总监连夜改RedLock方案

ZooKeeper:智能叫号式锁(优雅排队版)核心原理:医院挂号系统临时节点:挂号成功生成就诊号(临时节点),患者离开自动销号(Session断开)顺序节点:挂号单自动排序,1号就诊完自动通知2号// Java伪代码(使用Curator框架)InterProcessMutex lock = new InterProcessMutex(client, "/车位竞争");if(lock.acquire(30, TimeUnit.SECONDS)){ try { // 停車操作 } finally { lock.release(); }}

三大杀手锏:

自杀机制:客户端失联自动释放锁(比Redis的过期更及时)排队监控:可查看当前等待队列,支持公平锁事件通知:前车离开自动唤醒后车,避免无效轮询

血泪教训:某金融系统用ZooKeeper锁处理交易,因网络抖动导致Session过期,触发连环锁释放,最终资损百万

红蓝对抗:两大锁王终极PK

维度

Redis锁

ZooKeeper锁

性能

10w+ QPS(适合高频短操作)

1w QPS(适合低频长事务)

可靠性

依赖异步复制,主从切换可能丢锁

强一致性,CP模型保障

功能扩展

需自行实现读写锁、重入锁

原生支持公平锁、共享锁

运维成本

只需Redis集群

需维护ZooKeeper集群+观察者节点

经典选型场景:

秒杀库存扣减:用Redis锁(高频+允许极少数超卖)资金转账操作:用ZooKeeper锁(强一致性要求)新型混合战术:双锁合璧方案

设计思路:

用Redis做第一层过滤(快速拦截90%请求)用ZooKeeper做最终仲裁(保障关键操作)// Go伪代码示例func DoubleLock() { // 第一关:Redis快速锁 if redis.SetNX("order_123", token, 10s) { // 第二关:ZooKeeper强一致锁 if zk.CreateEphemeral("/order_123") { processOrder() // 真实业务处理 } }}

某物流公司实战数据:

纯Redis方案:吞吐量15w QPS,但每月发生2-3次锁异常混合方案:吞吐量12w QPS,半年0锁故障老司机忠告:锁的十大潜规则永远给锁设过期时间(防止死锁)解锁要验证身份(Lua脚本原子操作)网络延迟是头号敌人(时钟漂移要监控)重试次数超过3次必须告警锁日志要带唯一ID(方便链路追踪)别在锁里执行SQL事务(缩短临界区)定期演练锁故障(断网、宕机模拟)监控锁等待时间(超过1秒要优化)读写分离场景用共享锁能用队列就别用锁
0 阅读:0
南春编程

南春编程

感谢大家的关注