Generated comprehensive interview preparation materials covering: - Distributed systems (transactions, locks, ID generation, consistency) - Database (indexing, sharding, replication, transactions) - Caching (Redis, cache problems, distributed lock) - Message queues (RocketMQ, Kafka) - Concurrency (ThreadLocal, ConcurrentHashMap, thread pools) - JVM (GC, memory, tuning) - System design (seckill, short URL, IM, feed, LBS) - Algorithms (B+ tree, LRU, Red-Black tree, Skip list, Timing wheel) - Network (TCP/IP, HTTP/HTTPS) - Security (encryption, SQL injection, XSS) - Performance tuning - Design patterns - Microservices (Spring Boot, Gateway, Service Mesh) - Container orchestration (Kubernetes, Docker) - CI/CD, observability Each file includes: - Detailed questions - Comprehensive answers - Code examples - Real project experience - Alibaba P7 level requirements Generated with [Claude Code](https://claude.com/claude-code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
10 KiB
10 KiB
分布式锁
问题
- 什么是分布式锁?为什么需要分布式锁?
- 如何用 Redis 实现分布式锁?
- Redis 分布式锁有哪些坑?如何解决?
- Zookeeper 如何实现分布式锁?
- Redis 分布式锁和 Zookeeper 分布式锁的区别?
- Redisson 是如何实现分布式锁的?
- 在实际项目中如何使用分布式锁?
标准答案
1. 为什么需要分布式锁
场景
单机锁(synchronized、ReentrantLock):
@Service
public class OrderService {
private synchronized void createOrder(Order order) {
// 单机环境下有效
// 分布式环境下无效(不同 JVM)
}
}
问题:
- 单机锁只对单个 JVM 有效
- 分布式环境下,多个实例的锁互不排斥
- 需要跨 JVM、跨机器的锁机制
2. Redis 分布式锁
基本实现
public class RedisDistributedLock {
private final StringRedisTemplate redisTemplate;
// 加锁
public boolean lock(String key, String value, long expireTime) {
// SET key value NX PX expireTime
Boolean success = redisTemplate.opsForValue()
.setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS);
return success != null && success;
}
// 释放锁
public boolean unlock(String key, String value) {
// Lua 脚本:保证原子性
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
DefaultRedisScript<Long> script = new DefaultRedisScript<>(luaScript, Long.class);
Long result = redisTemplate.execute(script, Collections.singletonList(key), value);
return result != null && result == 1;
}
}
使用示例
@Service
public class OrderService {
@Autowired
private RedisDistributedLock lock;
public void createOrder(Order order) {
String lockKey = "order:" + order.getProductId();
String lockValue = UUID.randomUUID().toString();
try {
// 加锁(过期时间 30 秒)
boolean locked = lock.lock(lockKey, lockValue, 30000);
if (!locked) {
throw new BusinessException("系统繁忙,请稍后再试");
}
// 执行业务逻辑
// 1. 查询库存
// 2. 扣减库存
// 3. 创建订单
} finally {
// 释放锁
lock.unlock(lockKey, lockValue);
}
}
}
3. Redis 分布式锁的坑
坑 1:锁超时问题
场景:
线程 A 获取锁(设置 30 秒过期)
线程 A 执行业务(耗时 50 秒)
30 秒后,锁自动过期
线程 B 获取锁
线程 A 执行完毕,释放锁(把线程 B 的锁释放了!)
解决方案:看门狗(Watchdog)
原理:
- 启动后台线程
- 定期检查锁是否还存在
- 如果存在,续期(延长过期时间)
Redisson 实现:
RLock lock = redisson.getLock("myLock");
lock.lock(); // 自动续期(默认 30 秒)
try {
// 业务逻辑
} finally {
lock.unlock();
}
坑 2:主从切换导致锁丢失
场景:
线程 A 在 Master 获取锁
锁未同步到 Slave
Master 宕机,Slave 升级为 Master
线程 B 在新 Master 上获取锁(冲突!)
解决方案:Redlock(Redis 分布式锁算法)
原理:
- 向多个独立的 Redis 节点请求加锁
- 大多数节点加锁成功才算成功
// Redisson Redlock
RLock lock1 = redisson1.getLock("myLock");
RLock lock2 = redisson2.getLock("myLock");
RLock lock3 = redisson3.getLock("myLock");
RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
try {
redLock.lock();
// 业务逻辑
} finally {
redLock.unlock();
}
坑 3:释放锁时误删
场景:
线程 A 获取锁
线程 A 执行时间过长,锁超时过期
线程 B 获取锁
线程 A 执行完毕,释放锁(删除了线程 B 的锁!)
解决方案:Lua 脚本 + 唯一标识
-- Lua 脚本(原子操作)
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
Java 实现:
public boolean unlock(String key, String value) {
String luaScript =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
DefaultRedisScript<Long> script = new DefaultRedisScript<>(luaScript, Long.class);
Long result = redisTemplate.execute(script, Collections.singletonList(key), value);
return result != null && result == 1;
}
4. Zookeeper 分布式锁
原理
利用 Zookeeper 的临时顺序节点:
/lock
/lock/node-0000000001 (客户端 1)
/lock/node-0000000002 (客户端 2)
/lock/node-0000000003 (客户端 3)
流程:
- 客户端创建临时顺序节点
- 获取所有子节点,判断自己是否是序号最小的
- 如果是最小节点,获取锁
- 如果不是,监听前一个节点的删除事件
Curator 实现
@Component
public class ZkDistributedLock {
private final InterProcessMutex lock;
public ZkDistributedLock(CuratorFramework curatorFramework) {
this.lock = new InterProcessMutex(curatorFramework, "/lock");
}
public void acquire() {
try {
lock.acquire();
} catch (Exception e) {
throw new RuntimeException("获取锁失败", e);
}
}
public void release() {
try {
lock.release();
} catch (Exception e) {
throw new RuntimeException("释放锁失败", e);
}
}
}
使用:
@Service
public class OrderService {
@Autowired
private ZkDistributedLock lock;
public void createOrder(Order order) {
try {
lock.acquire();
// 业务逻辑
} finally {
lock.release();
}
}
}
5. Redis vs Zookeeper
| 特性 | Redis | Zookeeper |
|---|---|---|
| 性能 | 高(内存操作) | 低(磁盘 + ZAB 协议) |
| 可靠性 | 中(可能丢锁) | 高(CP 一致性) |
| 实现复杂度 | 简单 | 复杂 |
| 获取锁方式 | 轮询 | Watcher 通知(事件驱动) |
| 锁释放 | 超时自动释放 | 会话结束自动释放 |
| 适用场景 | 高并发、对一致性要求不高 | 严格一致性要求 |
6. Redisson 原理
看门狗(自动续期)
RLock lock = redisson.getLock("myLock");
lock.lock(); // 默认 leaseTime = -1(启用看门狗)
原理:
1. 加锁成功,启动看门狗线程
2. 看门狗每 10 秒检查一次
3. 如果锁还存在,续期 30 秒
4. 客户端宕机,会话结束,看门狗停止,锁自动过期
公平锁
RLock fairLock = redisson.getFairLock("myLock");
fairLock.lock();
原理:
- 加锁时,添加到队列尾部
- 按照请求顺序获取锁
- 性能比非公平锁低
读写锁
RReadWriteLock rwLock = redisson.getReadWriteLock("myLock");
rwLock.readLock().lock(); // 读锁(共享)
rwLock.writeLock().lock(); // 写锁(独占)
7. 实际项目应用
场景 1:秒杀系统
@Service
public class SeckillService {
@Autowired
private RedissonClient redisson;
public void seckill(Long userId, Long productId) {
RLock lock = redisson.getLock("seckill:" + productId);
try {
// 尝试加锁(最多等待 3 秒,锁自动释放时间 10 秒)
boolean locked = lock.tryLock(3, 10, TimeUnit.SECONDS);
if (!locked) {
throw new BusinessException("抢购人数过多,请稍后再试");
}
// 1. 查询库存
Integer stock = redisTemplate.opsForValue().get("stock:" + productId);
if (stock == null || stock <= 0) {
throw new BusinessException("库存不足");
}
// 2. 扣减库存
redisTemplate.opsForValue().decrement("stock:" + productId);
// 3. 创建订单
createOrder(userId, productId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new BusinessException("系统异常");
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
场景 2:定时任务分布式锁
@Component
public class ScheduledTask {
@Autowired
private RedissonClient redisson;
@Scheduled(cron = "0 */5 * * * ?") // 每 5 分钟
public void execute() {
RLock lock = redisson.getLock("scheduled-task");
try {
// 只有一个实例执行
boolean locked = lock.tryLock(0, 5, TimeUnit.MINUTES);
if (!locked) {
return; // 其他实例正在执行
}
// 执行任务
doTask();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
8. 阿里 P7 加分项
深度理解:
- 理解 CAP 理论在分布式锁中的体现
- 理解 Redis 的 ZSet 实现延迟队列的原理
- 理解 ZAB 协议和 ZK 的一致性保证
实战经验:
- 有处理分布式锁死锁的经验
- 有分布式锁性能优化的经验
- 有分布式锁监控的经验
架构能力:
- 能设计高可用的分布式锁方案
- 能设计分布式锁的容灾方案
- 能设计分布式锁的降级方案
技术选型:
- 了解 Redis、Zookeeper、Etcd 等多种分布式锁实现
- 能根据业务特点选择合适的方案
- 有自研分布式锁的经验