软考架构师复习站

缓存与分布式锁:高并发题的固定套路

缓存解决读压力,锁解决分布式并发互斥。两者都要会讲“问题本质、触发条件、解决方案、风险”。

本页脑图:缓存高并发与锁

缓存与锁

穿透

  • 数据不存在
  • 参数校验
  • 布隆过滤器
  • 缓存空值

击穿

  • 热点 Key 过期
  • 互斥锁
  • 逻辑过期
  • 热点永不过期

雪崩

  • 大量 Key 失效
  • TTL 随机
  • 多级缓存
  • Redis 高可用

分布式锁

  • 数据库
  • Redis
  • ZooKeeper
  • 唯一 value

穿透查不存在,击穿打热点,雪崩倒一片。

缓存穿透、击穿、雪崩

问题本质触发原因危害解决方案
穿透缓存没有,数据库也没有,请求直接打库。恶意非法 ID、业务异常参数。持续压垮数据库。参数校验、布隆过滤器、缓存空值、鉴权限流。
击穿单个热点 Key 失效,大量请求同时打库。热点商品、热点 Token、缓存过期。瞬间压垮数据库。互斥锁、逻辑过期、热点 Key 永不过期、预热。
雪崩大量 Key 同时失效或缓存集群宕机。TTL 集中过期、Redis 故障。系统级崩溃。TTL 随机化、多级缓存、高可用集群、熔断降级。

口诀:穿透查不存在,击穿打热点,雪崩倒一片。

缓存防御体系:入口、缓存、数据库、服务四层

  1. 入口层:参数校验、鉴权、验证码、IP 限流,先拦明显异常请求。
  2. 缓存层:布隆过滤器防穿透,逻辑过期防击穿,TTL 随机化防雪崩。
  3. 数据层:读写分离、索引优化、分库分表,避免数据库成为唯一瓶颈。
  4. 服务层:熔断、降级、隔离、告警,确保数据库扛不住时系统不被拖垮。
案例题写法:按请求链路从前往后写,不要只写 Redis。高并发优化最好按 CDN/网关/应用/缓存/数据库/消息队列/监控这条链路组织答案。

高并发链路细化

链路层次典型措施解决问题易错点
客户端/边缘静态资源 CDN、页面静态化、客户端限流减少源站压力,降低延迟。CDN 适合静态或可缓存内容,不适合强实时私密数据。
入口网关鉴权、限流、黑名单、验证码、WAF挡住恶意流量和超过系统能力的流量。限流不是越严越好,要结合业务优先级。
应用层线程池隔离、熔断降级、异步处理、批量合并防止局部慢调用拖垮整个服务。重试要有限制,否则会放大流量。
缓存层多级缓存、预热、逻辑过期、布隆过滤器降低数据库读压力。缓存要考虑一致性和失效策略。
数据库层索引、读写分离、分库分表、分区提升查询和写入能力。分库分表会带来跨库事务和查询复杂度。
消息层Kafka/RabbitMQ 削峰、异步落库把瞬时流量变成平滑处理。要处理幂等、重复消费和消息堆积。

为什么需要分布式锁

单机锁只能保护一个 JVM 内部的线程安全;服务多实例部署后,不同机器上的线程会同时操作同一份资源,例如库存扣减、定时任务抢占、热点缓存重建,这时需要跨进程、跨机器的互斥机制。

实现原理优点缺点适用
数据库唯一索引或事务锁。简单,依赖少,一致性强。性能低,死锁清理麻烦,数据库压力大。低并发、高一致批处理。
RedisSET key value NX PX timeout 原子加锁。性能高,工程实践广。主从切换极端情况下可能丢锁。秒杀、限流、热点缓存重建。
ZooKeeper临时顺序节点 + Watch。一致性强,天然防死锁,公平。吞吐较低,运维复杂。任务调度、配置协调、强一致场景。

Redis 锁必须会写的细节

  • 加锁原子性:必须用 SET key value NX PX timeout,不能先 setnx 再 expire,因为中间宕机会死锁。
  • value 唯一:用 UUID 标识持锁客户端,释放锁时先比较 value 再删除,防止误删别人的锁。
  • 释放原子性:比较和删除应使用 Lua 脚本保证原子。
  • 锁过期:设置过期时间防止客户端宕机永久持锁。
  • 锁续期:Redisson 看门狗可自动续期,防止业务未执行完锁先过期。
风险点:Redis 主从异步复制下,主节点加锁成功后尚未同步给从节点就宕机,从节点升主可能导致另一个客户端也拿到锁。强一致场景优先 ZooKeeper 或数据库事务方案。

Redis 锁 Lua 释放逻辑

释放锁不能直接 DEL key,因为可能误删别人后来获得的锁。正确逻辑是“先比较 value 是否等于自己的 UUID,再删除”。比较和删除要原子执行。

if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end

缓存一致性常见方案

Cache Aside

读:先读缓存,未命中查库并回填。写:先更新数据库,再删除缓存。工程最常见。

延迟双删

更新数据库后删除缓存,延迟一小段时间再删一次,降低并发读写造成脏缓存的概率。

消息通知

数据库变更后发送消息,缓存服务订阅并失效或刷新缓存。

binlog 订阅

通过 Canal 等订阅数据库 binlog,异步刷新缓存或搜索索引。

论文写法:秒杀/高并发场景

流量入口削峰 → 热点数据缓存 → 库存扣减互斥 → 异步下单 → 监控降级。

示例:在秒杀场景中,我首先通过 CDN 和网关限流过滤无效流量;其次将商品、库存和活动配置预热到 Redis,并对热点 Key 采用逻辑过期和互斥锁重建;库存扣减通过 Redis 原子操作和分布式锁保证不超卖;下单流程通过消息队列异步落库,实现削峰填谷;当数据库延迟超过阈值时触发熔断降级,返回排队提示。