侧边栏壁纸
博主头像
colo

欲买桂花同载酒

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

设计一个支持高并发的商品详情页缓存系统

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

题目

设计一个支持高并发的商品详情页缓存系统

信息

  • 类型:问答
  • 难度:⭐⭐

考点

缓存策略选择, 缓存失效处理, 缓存穿透防护, 数据一致性, 高并发优化

快速回答

设计要点:

  • 缓存策略:采用懒加载 + 主动刷新组合策略
  • 缓存失效:设置阶梯过期时间(基础30分钟 + 随机偏移)
  • 防穿透:布隆过滤器拦截无效请求 + 空值缓存
  • 一致性:双删策略 + 延迟消息队列
  • 高并发:Redis集群分片 + 本地二级缓存
## 解析

1. 核心设计原理

业务场景:电商平台商品详情页访问QPS 10万+,数据库无法直接承受压力。需要设计缓存系统:
• 降低数据库负载
• 保证数据最终一致性
• 防止恶意请求穿透

2. 缓存策略设计

// 伪代码:缓存获取逻辑
public Product getProduct(String id) {
  // 1. 先查本地缓存(如Caffeine)
  Product product = localCache.get(id);
  if (product != null) return product;

  // 2. 查Redis(防止缓存穿透)
  String redisKey = "product:" + id;
  String json = redis.get(redisKey);
  if (json != null) {
    if ("NULL".equals(json)) return null; // 空值标识
    return parseJson(json);
  }

  // 3. 使用分布式锁防击穿
  if (lock.tryLock(id)) {
    try {
      // 双重检查
      json = redis.get(redisKey);
      if (json == null) {
        // 4. 查数据库
        product = db.query("SELECT * FROM products WHERE id=?");
        if (product == null) {
          redis.setex(redisKey, 300, "NULL"); // 缓存空值
        } else {
          redis.setex(redisKey, 1800 + random(600), toJson(product)); // 基础30分钟+随机偏移
          localCache.put(id, product); // 写入本地缓存
        }
      }
    } finally {
      lock.unlock(id);
    }
  }
  return product;
}

3. 缓存失效与更新

双删策略保证一致性
1. 更新数据库前先删除缓存
2. 执行数据库更新
3. 延迟删除缓存(通过消息队列)

// 更新商品示例
public void updateProduct(Product product) {
  // 第一次删除
  redis.del("product:" + product.id);

  // 更新数据库
  db.update(product);

  // 发送延迟消息(5秒后二次删除)
  messageQueue.sendDelayMsg("delete_cache", product.id, 5000);
}

// 消息消费者处理
void handleDelayMsg(String id) {
  redis.del("product:" + id);
}

4. 防护异常场景

问题解决方案
缓存穿透• 布隆过滤器拦截非法ID
• 空值缓存(设置短过期时间)
缓存雪崩• 阶梯过期时间(基础值+随机偏移)
• 热点数据永不过期+后台刷新
缓存击穿• 分布式锁控制单请求回源
• 逻辑过期时间(实际数据永不过期)

5. 最佳实践

  • 多级缓存:本地缓存(Caffeine/Guava) + 分布式缓存(Redis集群)
  • 热点探测:实时监控热点Key,自动推送到本地缓存
  • 容量规划:Redis内存使用不超过70%,设置淘汰策略为allkeys-lru
  • 监控报警:缓存命中率 < 90% 或 穿透请求 > 100次/分钟时触发告警

6. 常见错误

  • ❌ 直接设置永久缓存 → 导致脏数据长期存在
  • ❌ 先更新数据库再删缓存 → 可能读到旧数据
  • ❌ 单点使用布隆过滤器 → 分布式环境失效
  • ❌ 本地缓存无过期时间 → 内存泄漏风险

7. 扩展知识

  • 数据分片:商品ID哈希分片到不同Redis集群节点
  • 读写分离:读请求走从节点,写请求走主节点
  • 持久化策略:AOF每秒刷盘 + RDB每日备份
  • 新架构探索:Redis6.0多线程IO / RedisCell限流模块