news 2026/6/3 3:54:57

【大白话说Java面试题 第91题】【Mysql篇】第21题:分布式锁的使用场景和原理?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【大白话说Java面试题 第91题】【Mysql篇】第21题:分布式锁的使用场景和原理?

📌PDF:大白话说Java面试题 — 03-Mysql篇

第21题:分布式锁的使用场景和原理

📚回答:

  • 核心考点
    大厂面试要求深入理解分布式锁的适用场景实现原理常见问题与解决方案,并能根据不同场景进行技术选型。面试官常追问:“Redis锁的过期时间怎么设置?”、“Redlock算法是什么?”、“ZooKeeper锁的羊群效应怎么解决?”

1. 分布式锁的核心概念

定义:分布式锁是控制分布式系统中多个进程/线程对共享资源互斥访问的协调机制,保证同一时刻只有一个客户端持有锁。

为什么需要分布式锁?

场景单机分布式
锁机制JVM锁(synchronized、ReentrantLock)跨进程/跨节点的分布式锁
问题多线程竞争共享资源多实例竞争共享资源(数据库、缓存、文件)

三大核心特性

特性说明重要性
互斥性同一时刻只有一个客户端能持有锁必须满足
可重入性同一客户端可重复获取已持有的锁按需
高可用锁服务本身不能成为单点故障必须满足
防死锁锁持有者宕机时,锁能自动释放必须满足
高性能加锁解锁延迟低、吞吐量高必须满足
2. 核心使用场景

2.1 库存扣减(防止超卖)

// 电商秒杀场景publicvoidreduceStock(LongproductId,Integerquantity){StringlockKey="lock:product:stock:"+productId;// 获取分布式锁booleanlocked=redisLock.lock(lockKey,3000);if(!locked){thrownewBusinessException("系统繁忙,请稍后重试");}try{// 查询库存intstock=productMapper.selectStock(productId);if(stock<quantity){thrownewBusinessException("库存不足");}// 扣减库存productMapper.updateStock(productId,stock-quantity);}finally{redisLock.unlock(lockKey);}}

2.2 分布式定时任务(防止重复执行)

@Scheduled(cron="0 0 2 * * ?")// 凌晨2点执行publicvoiddoDailyReport(){StringlockKey="lock:job:dailyReport";// 尝试获取锁,获取成功才执行if(redisLock.tryLock(lockKey,0,TimeUnit.SECONDS)){try{generateReport();// 生成日报}finally{redisLock.unlock(lockKey);}}else{log.info("另一实例正在执行,跳过");}}

2.3 防止缓存击穿(缓存重建互斥)

publicStringgetData(Stringkey){Stringvalue=redis.get(key);if(value!=null){returnvalue;}// 缓存失效,尝试获取锁StringlockKey="lock:cache:rebuild:"+key;if(redisLock.tryLock(lockKey,1000)){try{// 双重检查value=redis.get(key);if(value!=null)returnvalue;// 从数据库加载value=loadFromDB(key);redis.setex(key,3600,value);returnvalue;}finally{redisLock.unlock(lockKey);}}else{// 等待片刻后重试Thread.sleep(100);returngetData(key);}}

2.4 其他场景

场景示例说明
唯一性校验订单号生成、防重复提交防止分布式下ID重复
分布式ID生成雪花算法workerID分配保证workerID全局唯一
配置动态更新Apollo/Nacos配置发布同一时刻只一个节点发布
3. Redis分布式锁的实现原理

3.1 基础版本(SETNX + EXPIRE)

// 问题:非原子操作,可能SETNX后崩溃导致锁永不释放Booleansuccess=redis.setnx(lockKey,clientId);if(success){redis.expire(lockKey,30);// 如果这步崩溃,锁永不释放}

3.2 原子版本(SET NX EX)

// Redis 2.6.12+ 支持原子操作Stringresult=redis.set(lockKey,clientId,"NX","EX",30);// NX:不存在才设置// EX:过期时间30秒

3.3 完整实现要点

publicclassRedisDistributedLock{// 获取锁publicbooleanlock(Stringkey,Stringvalue,intexpireSec){Stringresult=jedis.set(key,value,"NX","EX",expireSec);return"OK".equals(result);}// 释放锁(需要Lua脚本保证原子性,防止误删其他线程的锁)publicbooleanunlock(Stringkey,Stringvalue){StringluaScript="if redis.call('get', KEYS[1]) == ARGV[1] then "+" return redis.call('del', KEYS[1]) "+"else "+" return 0 "+"end";Objectresult=jedis.eval(luaScript,Collections.singletonList(key),Collections.singletonList(value));returnLong.valueOf(1).equals(result);}}

3.4 Redis锁的常见问题与解决方案

问题原因解决方案
锁误释放线程A的锁过期,线程B获取锁,线程A释放时删了B的锁释放时校验value(客户端标识)
锁过期业务未完成业务执行时间超过锁过期时间看门狗(WatchDog)自动续期
主从切换锁丢失Redis主从异步复制,主宕机锁未同步到从Redlock算法(多节点)
不可重入同一线程重复获取同一锁失败ThreadLocal存储重入次数
阻塞获取获取锁失败立即返回自旋重试(需退避算法)

看门狗实现

// 获取锁后启动定时任务,在锁过期前1/3时间续期ScheduledExecutorServicescheduler=Executors.newScheduledThreadPool(1);scheduler.scheduleAtFixedRate(()->{if(isLockHeld()){jedis.expire(lockKey,expireSec);// 续期}},expireSec/3,expireSec/3,TimeUnit.SECONDS);
4. Redlock算法(Redis作者推荐)

4.1 核心原理

Redlock是Redis作者提出的分布式锁算法,解决Redis主从切换导致锁丢失问题。

工作流程

  1. 获取当前时间戳(毫秒)
  2. 依次向N个(通常5个)独立的Redis节点尝试获取锁
  3. 当成功获取锁的节点数 > N/2(多数派)且总耗时 < 锁有效期时,认为获取锁成功
  4. 锁的有效期 = 初始有效期 - 获取锁耗时
  5. 释放锁时,向所有节点发送释放请求

4.2 Redlock优缺点

优点缺点
高可用:少数节点宕机不影响性能低:需要多节点网络通信
强一致性:多数派决策时钟漂移问题:依赖节点时间同步
自动失效:自带TTL实现复杂:需要维护多个连接

生产建议:绝大多数场景不需要Redlock,单节点Redis + 主从 + 看门狗已足够。Redlock只在金融级强一致性场景考虑。

5. ZooKeeper分布式锁实现原理

5.1 核心机制

ZooKeeper的**临时顺序节点(Ephemeral Sequential Node)**特性天然适合分布式锁。

工作流程

  1. 客户端在锁路径下创建临时顺序节点(如/lock/seq-000001
  2. 获取该路径下所有子节点,判断自己是否是序号最小的节点
  3. 是 → 获得锁;否 → 监听前一个节点的删除事件
  4. 前一个节点删除后,再次判断自己是否最小(重复步骤2)

代码示例

publicclassZooKeeperDistributedLock{publicvoidlock(StringlockPath)throwsException{StringcurrentPath=zk.create(lockPath+"/seq-",null,ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);List<String>children=zk.getChildren(lockPath,false);Collections.sort(children);StringminNode=children.get(0);if(currentPath.endsWith(minNode)){// 获得锁return;}else{// 监听前一个节点StringprevNode=getPrevNode(currentPath,children);CountDownLatchlatch=newCountDownLatch(1);zk.exists(lockPath+"/"+prevNode,event->latch.countDown());latch.await();// 阻塞等待// 重新尝试获取锁(递归)lock(lockPath);}}}

5.2 Redis vs ZooKeeper对比

对比维度RedisZooKeeper
性能极高(内存,单机10万+ QPS)一般(1万+ QPS)
一致性最终一致(主从异步)强一致(ZAB协议)
可靠性主从切换可能丢锁(多数派写入)
实现复杂度
依赖Redis集群ZooKeeper集群
自动续期需自己实现看门狗原生支持(临时节点)
适用场景高并发、高性能场景强一致性场景
6. 分布式锁选型对比
方案性能一致性可用性复杂度典型场景
Redis单机极高开发/测试
Redis主从极高中(可能丢锁)高并发业务(99%场景)
Redlock金融级强一致性
ZooKeeper极高强一致性要求(配置中心)
数据库唯一索引简单场景、无额外依赖

选型决策树

是否需要极高性能(10万+ QPS)? ├── 是 → Redis主从 + 看门狗 └── 否 → 是否需要强一致性? ├── 是 → ZooKeeper / Redlock └── 否 → Redis主从
7. 常见问题与解决方案

Q1:锁过期时间怎么设置?

A:设置为业务执行时间的2-3倍,且配合看门狗自动续期。经验值:秒杀场景100-300ms,缓存重建1-3秒。

Q2:获取锁失败怎么处理?

A:根据业务决定:

  • 快速失败:立即返回"系统繁忙"(秒杀场景)
  • 阻塞等待:自旋重试,使用退避算法(指数退避)
  • 排队等待:使用消息队列

Q3:Redis分布式锁怎么实现可重入?

A:使用ThreadLocal存储锁持有信息:

ThreadLocal<Map<String,Integer>>lockCount=...;publicbooleanlock(Stringkey){if(lockCount.get().containsKey(key)){lockCount.get().put(key,count+1);returntrue;}// 尝试获取Redis锁...}

Q4:ZooKeeper锁的羊群效应如何解决?

A:不监听所有子节点,只监听前一个节点,避免所有客户端同时被唤醒。


💡面试官想要的满分总结

"分布式锁是分布式系统中协调共享资源访问的核心机制。

核心场景

  • 库存扣减(防超卖)
  • 分布式定时任务(防重复执行)
  • 缓存击穿防护(单实例重建)

主流实现

  • RedisSET NX EX原子操作,高性能(10万+ QPS),需处理锁过期、误释放、不可重入等问题,通过看门狗自动续期防业务超时
  • ZooKeeper:临时顺序节点,强一致性,自动释放,性能较低(1万+ QPS),适合强一致性场景

关键技术

  • 防死锁:设置TTL/临时节点
  • 防误释放:释放时校验客户端标识(Lua脚本)
  • 锁续期:看门狗(WatchDog)
  • 多数派算法:Redlock解决主从切换丢锁

选型建议

  • 高并发业务(99%场景)→Redis主从 + 看门狗
  • 金融级强一致性 →ZooKeeper / Redlock

一句话:分布式锁的核心是互斥、防死锁、高可用;Redis高性能适合大多数场景,ZooKeeper强一致适合核心金融系统。"


觉得对您有帮助,麻烦点点关注啦,您的关注是我创作的最大动力~ 🎯

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/3 3:50:57

Windows蓝牙连接PS3控制器终极指南:BthPS3驱动完整解决方案

Windows蓝牙连接PS3控制器终极指南&#xff1a;BthPS3驱动完整解决方案 【免费下载链接】BthPS3 Windows kernel-mode Bluetooth Profile & Filter Drivers for PS3 peripherals 项目地址: https://gitcode.com/gh_mirrors/bt/BthPS3 你是否曾经在Windows电脑上尝试…

作者头像 李华