① 分布式锁的江湖地位
pie
title 2024分布式锁使用率
"Redis" : 68
"Zookeeper" : 25
"Etcd" : 5
"其他" : 2
经典场景:
"当100个程序员同时点击秒杀按钮,如何让系统觉得他们很有素质在排队?"
② Redis分布式锁(Redisson实战)
完整可运行代码:
// 前置:pom.xml添加依赖
// <dependency>
// <groupId>org.redisson</groupId>
// <artifactId>redisson</artifactId>
// <version>3.17.0</version>
// </dependency>
public class RedisLockDemo {
public static void main(String[] args) throws InterruptedException {
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("myLock");
// 尝试加锁,最多等待10秒,锁自动释放时间30秒
boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (isLocked) {
try {
System.out.println("抢锁成功!开始执行秒杀逻辑");
// 模拟业务处理
Thread.sleep(2000);
System.out.println("已扣除库存:iPhone 15 Pro 1台");
} finally {
lock.unlock();
System.out.println("释放锁,深藏功与名");
}
} else {
System.out.println("手速太慢,下次一定!");
}
redisson.shutdown();
}
}
代码彩蛋:
运行后查看Redis数据变化:
127.0.0.1:6379> keys *
1) "myLock" # 看门狗机制自动续期
③ Zookeeper分布式锁(Curator实现)
// 前置:pom.xml添加依赖
// <dependency>
// <groupId>org.apache.curator</groupId>
// <artifactId>curator-recipes</artifactId>
// <version>5.3.0</version>
// </dependency>
public class ZkLockDemo {
public static void main(String[] args) {
CuratorFramework client = CuratorFrameworkFactory.newClient(
"localhost:2181",
new ExponentialBackoffRetry(1000, 3)
);
client.start();
InterProcessMutex lock = new InterProcessMutex(client, "/locks/iphone15");
try {
if (lock.acquire(10, TimeUnit.SECONDS)) {
System.out.println("获得zk锁,开始处理订单");
// 模拟业务处理
Thread.sleep(2000);
System.out.println("订单处理完毕,准备发货");
}
} finally {
lock.release();
System.out.println("优雅释放锁");
}
client.close();
}
}
执行过程可视化:
graph TD
A[客户端1] --> B(创建/locks/iphone15/_c_123)
C[客户端2] --> D(创建/locks/iphone15/_c_456)
B --> E{检查是否最小节点}
D --> E
E -->|是| F[获得锁]
E -->|否| G[监听前一个节点]
④ 皇位争夺战对比表
| 维度 | Redis(红皇帝) | Zookeeper(动物园国王) |
|---|---|---|
| 加锁速度 | 快如闪电(内存操作) | 较慢(需要创建节点) |
| 一致性 | 异步复制可能丢锁(需RedLock) | 强一致(ZAB协议) |
| 死锁处理 | 依赖超时自动释放 | 会话结束自动删除节点 |
| 惊群效应 | 发布订阅通知精准唤醒 | Watch机制可能引发羊群效应 |
| 运维成本 | 简单(单节点) | 需要集群部署 |
| 适用场景 | 高频低精度(秒杀/缓存) | 低频高可靠(资金交易) |
⑤ 血泪案例库
案例1:某电商秒杀事故
- 选择:Redis单节点锁
- 现象:主从切换导致超卖
- 数据:多卖出127台Switch
- 尸检报告:
[ERROR] 库存校验失败! |-- 预期库存: 100 |-- 实际库存: -27 |-- 事故原因: 主节点宕机,从节点未同步锁状态
案例2:银行转账系统故障
- 选择:Zookeeper锁未处理异常
- 现象:客户端崩溃导致锁永久持有
- 监控数据:
lock_hold_time = 86400s # 长达24小时 blocked_transactions = 3562 customer_complaints = 9527
⑥ 高频面试八连击
-
Redis分布式锁一定要用Redisson吗?
答:生产环境必须用,原生SETNX存在原子性问题和锁续期难题(展示错误案例:
SETNX + EXPIRE非原子操作) -
Zookeeper锁如何避免羊群效应?
答:使用临时有序节点,每个客户端只监听前一个节点(对比旧版做法:所有客户端监听同一个节点)
-
RedLock算法真的安全吗?
答:Martin Kleppmann曾质疑其时钟依赖问题,但在无时钟跳跃场景下仍可用(需至少3个独立Redis实例)
-
CP和AP系统如何选择锁?
答:CP系统(如ZK)适合金融交易,AP系统(如Redis)适合互联网高并发
-
如何实现可重入锁?
答:Redis用Hash结构记录线程ID和计数,ZK在节点数据中维护计数器
-
锁超时时间设置多少合适?
答:建议:业务最大耗时×2,配合看门狗自动续期(如Redisson默认30秒)
-
分布式锁和数据库锁有什么区别?
答:数据库锁(如行锁)粒度粗且影响性能,分布式锁更细粒度(对比案例:秒杀系统QPS从200提升到5000)
-
如果ZK锁客户端断连了怎么办?
答:会话超时(默认60s)后自动删除临时节点,需业务方处理中断异常(演示代码中try-finally块)
⑦ 灵魂拷问:你的系统适合哪种锁?
决策树:
graph TD
A[需要强一致性?] -->|是| B[选Zookeeper]
A -->|否| C{QPS超过1万?}
C -->|是| D[选Redis]
C -->|否| E{需要自动释放?}
E -->|是| D
E -->|否| B
经典组合:
- 金融系统:ZK锁 + 数据库事务
- 电商秒杀:Redis锁 + 库存预扣 + 队列削峰
- 物联网:Redis锁 + 本地缓存降级
⑧ 自测实验:制造锁竞争
步骤:
- 同时启动10个线程运行RedisLockDemo
- 观察输出:
线程1:抢锁成功! 线程2:手速太慢,下次一定! ... 线程5:释放锁,深藏功与名 - 查看Redis监控:
redis-cli info stats | grep instantaneous_ops_per_sec # 可见QPS突增现象
进阶挑战:
用Jepsen框架模拟网络分区,观察两种锁的可靠性差异
下一讲剧透:
《7.3 熔断降级:系统崩溃前的紧急逃生通道》——用Hystrix实现服务熔断,并手撕Sentinel的滑动窗口算法,教你设计一个会"装死"的智能系统!