侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1824 篇文章
  • 累计收到 0 条评论

设计一个支持千万级QPS的分布式缓存系统,要求解决缓存穿透、击穿、雪崩问题并实现热点数据自适应处理

2025-12-12 / 0 评论 / 2 阅读

题目

设计一个支持千万级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)