题目
设计一个支持千万级QPS的分布式缓存系统,要求解决缓存穿透、击穿、雪崩问题并实现热点数据自适应处理
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
分布式缓存架构设计,缓存异常场景处理,热点数据发现与处理,一致性哈希与负载均衡,高并发低延迟优化
快速回答
核心设计要点:
- 分层架构:客户端本地缓存 + 分布式缓存集群 + 持久化存储
- 异常防护:布隆过滤器防穿透,互斥锁防击穿,随机TTL防雪崩
- 热点处理:实时流量监控 + 热点数据分片复制 + 本地缓存备份
- 数据分布:虚拟节点一致性哈希 + 动态负载均衡
- 性能优化:异步IO模型 + 批处理操作 + 连接池优化
1. 系统架构设计
分层架构:
- 第一层:客户端本地缓存(Guava/Caffeine),处理50%以上读请求
- 第二层:分布式缓存集群(300+节点),采用分片架构
- 第三层:持久化存储(MySQL+分库分表)
数据分片:使用带虚拟节点的改进一致性哈希算法,确保节点增减时数据迁移量<10%
# 虚拟节点一致性哈希示例
class ConsistentHash:
def __init__(self, nodes, vnode_count=200):
self.ring = {}
self.vnode_count = vnode_count
for node in nodes:
self.add_node(node)
def add_node(self, node):
for i in range(self.vnode_count):
vnode_key = f"{node}-vnode{i}"
hash_val = self._hash(vnode_key)
self.ring[hash_val] = node
def get_node(self, key):
hash_val = self._hash(key)
sorted_keys = sorted(self.ring.keys())
for ring_key in sorted_keys:
if hash_val <= ring_key:
return self.ring[ring_key]
return self.ring[sorted_keys[0]]2. 缓存异常处理
2.1 缓存穿透
- 布隆过滤器:前置校验非法Key(误判率<0.1%)
- 空值缓存:对不存在的Key缓存空对象(TTL=30s)
- 请求合并:对相同缺失Key的请求进行合并
2.2 缓存击穿
- 互斥锁:使用Redis分布式锁控制单线程回源
// Java伪代码实现 public Object getData(String key) { Object value = cache.get(key); if (value == null) { if (redis.setnx(key+"_lock", 1)) { // 获取锁 value = db.query(key); cache.set(key, value, randomTTL()); redis.del(key+"_lock"); } else { Thread.sleep(50); // 等待重试 return getData(key); } } return value; } - 逻辑过期:缓存永不过期,后台异步更新
2.3 缓存雪崩
- 随机TTL:基础过期时间+随机偏移(如300s±10%)
- 多级缓存:本地缓存+分布式缓存同时失效概率低
- 熔断降级:当缓存失效超过阈值,直接返回默认值
3. 热点数据处理
热点发现:
- 实时监控:每个节点统计Top-K访问Key(Count-Min Sketch算法)
- 中心聚合:Zookeeper协调节点汇总全局热点Key
热点应对:
- 数据分片复制:将热点Key复制到多个节点(如1个Key→3个副本)
- 动态负载均衡:客户端根据热点分布动态路由请求
- 本地缓存备份:推送热点数据到应用层本地缓存
4. 高并发优化
- 网络模型:使用Netty实现Reactor模式(单机50万+连接)
- 批处理:合并多个get请求(mget)减少网络往返
- 连接池:维护长连接池(最大空闲连接=200)
- 序列化:使用Protobuf替代JSON(体积减少60%)
5. 最佳实践
- 监控告警:实时监控命中率(>95%)、延迟(<5ms P99)、错误率
- 渐进扩容:节点扩容后采用双写策略平滑迁移
- 分级存储:高频数据存内存,低频数据存SSD
- 读写分离:写请求路由到主节点,读请求分散到从节点
6. 常见错误
- ❌ 无底洞效应:节点过多导致批量查询效率下降 → 优化分片策略
- ❌ 缓存污染:低频数据驱逐热点数据 → 使用LFU淘汰策略
- ❌ 惊群效应:大量请求同时回源 → 采用互斥锁控制
- ❌ 不一致性:缓存与DB不一致 → 使用双删策略+消息队列同步
7. 扩展知识
- 数据一致性:采用Write-through模式+版本号校验
- 冷启动:使用LRU预加载热点数据
- 新兴方案:Redis6多线程IO,持久内存(PMEM)应用
- 容灾设计:跨机房部署,故障自动转移(Sentinel/Cluster)