本轮 Java分布式架构面试题库(带答案)
【面试题】1. 请详细解释CAP定理,并说明为什么在分布式系统中无法同时满足三个特性?
【标准答案】CAP定理指出分布式系统最多只能同时满足三项中的两项:一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)。为什么只能三选二?核心原因是网络分区是分布式系统的客观现实——网络总会出现延迟、中断或丢包。当分区发生时,系统面临两难选择:如果坚持一致性,就必须拒绝部分请求或阻塞等待,牺牲可用性;如果坚持可用性继续服务,节点间数据可能不一致,牺牲一致性。因此CP系统如Zookeeper、HBase在分区时优先保证一致性,可能暂停服务;AP系统如Cassandra、DynamoDB优先保证可用性,接受短暂不一致。实际架构中,我们往往根据业务场景在C和A之间权衡,比如金融交易强一致、社交点赞最终一致。
【面试题】2. Redis分布式锁的实现方式有哪些?SET NX EX方式和Redisson有什么区别?
【标准答案】Redis分布式锁主要有三种实现方式:SET NX EX是基础方案,通过SET key value NX PX milliseconds一条命令完成加锁,NX保证只有key不存在时才设置成功,PX设置过期时间防止死锁。但这个方案有几个问题:锁续期需要额外定时任务、锁释放可能误删其他线程的锁、不支持可重入。Redisson框架封装了更完善的实现:引入watch dog看门狗机制自动续期,默认锁过期时间30秒,后台线程每10秒续期;使用Redis Hash结构实现可重入锁,key存锁名,field存线程标识,value存重入次数;加锁Lua脚本保证原子性,释放锁时校验线程标识避免误删;主从架构下可使用Redlock算法,向多个主节点顺序加锁,多数成功才算获取锁成功。生产环境建议直接使用Redisson,它还提供读写锁、公平锁、红锁等多种变体。
【面试题】3. TCC分布式事务模式的核心原理是什么?如何处理空回滚、悬挂等异常场景?
【标准答案】TCC是Try-Confirm-Cancel的缩写,核心思想是将业务拆分为三个阶段:Try阶段预留资源但不提交,比如冻结账户余额而不是直接扣款;Confirm阶段确认执行业务,使用预留资源完成操作;Cancel阶段取消预留资源,释放冻结的资金。空回滚场景是指Try请求因网络超时未到达,但协调者发出了Cancel指令。解决方案是维护事务状态表,Cancel时检查是否存在Try记录,没有则插入一条空回滚记录阻止后续Try。悬挂问题是指Cancel比Try先到达执行,后续Try又成功预留资源。解决方案同样是检查状态表,如果已有Cancel记录则拒绝Try执行。幂等性保证是关键,因为网络重试可能导致重复调用,每个阶段都需要支持幂等,通常通过唯一事务ID和状态机实现,比如Confirm只能从Trying状态转到Confirmed。实际项目中,Seata框架的TCC模式提供了完整的解决方案,包括事务日志记录、幂等控制和异常处理。
【面试题】4. Snowflake雪花算法生成分布式ID的原理是什么?时钟回拨问题如何解决?
【标准答案】Snowflake生成64位Long型ID,结构为1位符号位(始终0) + 41位时间戳(毫秒级,可用69年) + 10位工作机器ID(5位数据中心ID + 5位机器ID,支持1024节点) + 12位序列号(毫秒内4096个ID)。核心原理是利用时间戳有序性保证ID趋势递增,机器ID保证分布式唯一性,序列号保证毫秒内并发生成。时钟回拨是最大风险:如果机器时间被回调,可能生成重复ID。解决方案有几种:短期回拨(几毫秒)可以等待时间追上上次ID时间戳,期间拒绝服务或返回错误;长时间回拨可以切换到备用方案如数据库号段模式,或使用Zookeeper管理机器ID并在回拨时重新分配;Leaf-segment等改进方案预分配ID号段,即使时钟回拨也不影响,因为不依赖机器时间。实际项目中建议使用Leaf或UidGenerator等成熟框架,它们已经处理了时钟回拨、机器ID分配等复杂问题。
【面试题】5. 缓存穿透、缓存击穿、缓存雪崩分别是什么?各自的解决方案是什么?
【标准答案】缓存穿透指查询不存在的数据,请求穿过缓存直达数据库。解决方案:布隆过滤器在查询前过滤掉不存在的key;或缓存空值设置短过期时间,但要配合布隆过滤器防止恶意攻击填充大量空缓存。缓存击穿指热点key过期瞬间大量并发请求击穿到数据库。解决方案:互斥锁重建缓存,只允许一个线程重建,其他等待或返回旧数据;逻辑过期方案在value中设置逻辑过期时间,由后台异步重建,永不过期物理key。缓存雪崩指大量key同一时间集中过期或Redis宕机。解决方案:过期时间加随机值避免集中过期;多级缓存本地缓存+分布式缓存;熔断降级保护数据库;Redis主从+哨兵或Cluster保证高可用。面试重点区分:穿透是查不到、击穿是热点过期、雪崩是大规模过期或宕机,三者成因不同,解决方案也有差异。
【面试题】6. Zookeeper分布式锁的实现原理是什么?与Redis分布式锁相比有什么优缺点?
【标准答案】Zookeeper分布式锁基于临时顺序节点和Watch机制实现。加锁流程:客户端在/lock节点下创建临时顺序节点,如/lock/seq-000001;获取/lock下所有子节点并排序;如果自己创建的节点序号最小,获取锁成功;否则Watch监听前一个节点的删除事件,前一个节点释放锁后收到通知再次尝试。释放锁时客户端删除自己的临时节点或会话断开,ZooKeeper自动删除临时节点,触发后续节点的Watch通知。相比Redis锁:优点是临时节点随会话自动删除,不存在锁过期但业务未执行完的问题,天然避免死锁;顺序节点保证公平锁特性,先到先得;ZooKeeper的强一致性保证锁状态可靠。缺点是性能较低,每次加锁释放都要集群多数派投票确认;需要维护ZooKeeper集群,运维成本高;Watch通知有延迟,极端情况下可能存在羊群效应。Redis锁性能更高适合高并发场景,ZooKeeper锁可靠性更高适合对一致性要求严格的场景如主备选举。
【面试题】7. 如何保证缓存与数据库的数据一致性?延时双删方案的核心思想是什么?
【标准答案】缓存与数据库一致性是个经典难题。先更新数据库再更新缓存的问题是并发时可能后更新的覆盖先更新的;先删缓存再更新数据库的问题是并发读可能把旧数据写入缓存;先更新数据库再删缓存的问题是删除失败导致缓存长期不一致。延时双删方案核心思想是第一次删除缓存 + 更新数据库 + 延时第二次删除缓存。第一次删除防止并发读加载旧数据到缓存;延时第二次删除解决更新数据库期间有读请求把旧数据写入缓存的问题,延时时间要大于数据库读取+写入缓存的耗时。具体实现可以用消息队列延时投递、定时任务或Canal监听binlog。更高要求的方案有Canal订阅数据库binlog异步删除缓存、消息队列保证删除可靠性、延时消息实现第二次删除。面试时要强调没有完美方案,一致性要求高的场景可以配合binlog监听、消息队列重试机制,业务允许最终一致性则延时双删足够。
【面试题】8. Raft算法的Leader选举流程是怎样的?如何解决Split Brain问题?
【标准答案】Raft将节点分为Leader、Follower、Candidate三种角色。正常情况Leader处理所有请求并同步日志给Follower,Follower被动响应Leader和Candidate的请求。选举流程:Follower启动时有个随机选举超时时间(150-300ms),如果在超时前没收到Leader心跳就转为Candidate,自增任期号并向其他节点发起投票请求(RequestVote);节点收到投票请求后,如果该任期未投票且Candidate日志至少和自己一样新就投票同意;Candidate收到多数派投票后成为新Leader,立即向所有节点发送心跳确认 leadership。Split Brain是网络分区导致多个Leader的问题,Raft通过多数派投票机制解决:任何选举都需要超过半数节点同意,分区后一侧节点不足半数无法选出新Leader,只有拥有多数派节点的一侧可以选举成功。Raft还保证安全性:只有日志最新的Candidate才能当选,通过RequestVote请求中的lastLogIndex和lastLogTerm字段校验。实际项目中etcd、Consul都基于Raft实现,Kubernetes的etcd集群至少需要3节点保证分区容错。
【面试题】9. Seata框架的AT模式和TCC模式有什么区别?分别适合什么场景?
【标准答案】Seata是阿里开源的分布式事务框架,支持AT、TCC、Saga、XA四种模式。AT模式(Automatic Transaction)核心是自动补偿,业务代码无侵入,只需要标注@GlobalTransactional注解。第一阶段执行业务SQL并记录前后镜像到undo_log表,本地事务直接提交;第二阶段提交直接删除undo_log,回滚时根据undo_log自动生成反向SQL补偿。优点是开发简单,业务代码几乎无侵入;缺点是依赖数据库事务,只能用于支持ACID的关系数据库,存在脏回滚问题需要防。TCC模式需要业务代码实现Try、Confirm、Cancel三个接口,Try阶段预留资源不提交,Confirm阶段确认提交,Cancel阶段回滚释放。优点是性能高锁粒度小,不依赖数据库事务可用于任意存储;缺点是业务侵入性大需要编写大量补偿代码。AT模式适合传统单体应用拆分微服务、对一致性要求高、开发效率优先的场景;TCC模式适合高并发性能敏感、跨多种存储类型、复杂业务流程需要精细化控制的场景,比如金融账户转账、库存扣减等核心业务。
【面试题】10. 分布式限流算法有哪些?令牌桶和漏桶的区别是什么?如何基于Redis实现分布式限流?
【标准答案】分布式限流主要算法有:固定窗口计数器,简单但存在边界突发问题;滑动窗口,精度高但内存开销大;令牌桶算法,以固定速率生成令牌放入桶中,请求获取令牌才允许通过,桶满则丢弃令牌,特点是允许一定突发流量;漏桶算法,请求先进入队列,以固定速率处理,超队列容量则拒绝,特点是平滑流量不允许突发。令牌桶与漏桶核心区别:令牌桶限制平均速率同时允许突发(桶内有令牌即可用),漏桶强制固定速率完全平滑流量;令牌桶关注入口限流,漏桶关注出口处理速率;令牌桶更适合网络流量整形,漏桶更适合数据库访问保护。Redis实现分布式限流方案:固定窗口用INCR计数+EXPIRE设置过期时间;滑动窗口用ZSET存储时间戳,ZRANGE移除过期请求,ZCARD统计当前窗口请求数;令牌桶用Lua脚本保证原子性,Lua中实现令牌生成、获取、判断是否允许通过。生产环境推荐Redis+Lua方案,脚本原子执行避免并发问题,配合Sentinel、Guava RateLimiter做本地限流作为第一层防护。限流后还要考虑降级策略:返回默认值、排队等待、服务降级页面等。