秒杀商品详情页被打挂:Redis 缓存击穿、热点 key 互斥锁与布隆过滤器的配置记录

简介: 热门商品缓存key过期瞬间几千请求打数据库导致CPU飙到95%。记录互斥锁SET NX EX、热点key逻辑过期、布隆过滤器挡穿透、TTL随机抖动防雪崩的完整配置,以及死锁、误判等踩坑。

上周商城搞活动,一款热门商品的详情页在开抢前 5 分钟突然变慢,数据库 CPU 从 20% 飙到 95%。我查 Redis 发现那个商品的缓存 key 刚好过期了,一瞬间几千个请求同时穿过缓存打到数据库,把连接池占满。

这种就是典型的缓存击穿——某个热点 key 失效瞬间,大量并发请求直接打数据库。前后花了两天把防护加上,记录下来。

一、先分清三个概念

缓存问题容易混,先理清楚:

  • 缓存穿透:查一个根本不存在的数据,缓存里没有,每次都打数据库(攻击者恶意查不存在的 ID)。
  • 缓存击穿:某个热点 key 过期瞬间,大量并发请求同时打数据库。
  • 缓存雪崩:大量 key 同时过期,整个缓存层失效,全打数据库。

这次是缓存击穿:单个热点商品的 key 过期了。

二、互斥锁:只让一个请求去重建缓存

热点 key 过期时,只放一个请求去查数据库重建缓存,其他请求等一下再读缓存:

// 伪代码
public Product getProduct(Long id) {
    String key = "product:" + id;
    Product p = redis.get(key);
    if (p != null) return p;

    // 缓存miss,抢锁
    String lockKey = "lock:product:" + id;
    try {
        // SET NX EX 10,只放一个请求成功
        Boolean locked = redis.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(locked)) {
            p = db.queryProduct(id);
            redis.setex(key, 3600, p);
            return p;
        } else {
            // 没抢到锁,等200ms再读缓存
            Thread.sleep(200);
            return getProduct(id);
        }
    } finally {
        redis.del(lockKey);
    }
}

关键参数:锁超时 10 秒(防止拿到锁的请求挂了死锁),没抢到锁的等 200ms 重试,别无限递归。

三、热点 key 永不过期:逻辑过期

对确定会很热的商品,缓存不设 TTL,改成逻辑过期——在 value 里存一个过期时间,后台异步更新:

// value 里存逻辑过期时间
{
  "product": {...},
  "expireAt": 1728000000000  // 逻辑过期时间
}

// 读的时候判断逻辑过期,异步更新
public Product getProductLogic(Long id) {
    String raw = redis.get("product:" + id);
    CacheData data = parse(raw);
    if (data.getExpireAt() < System.currentTimeMillis()) {
        // 逻辑过期,开异步线程更新缓存
        asyncRefresh(id);
    }
    return data.getProduct();  // 返回旧数据,用户无感知
}

这样缓存永远不会物理过期,不存在"击穿瞬间"。代价是用户可能读到几秒旧数据,商品详情页可以接受。

四、布隆过滤器:挡住查不存在 ID 的请求

缓存穿透是另一个问题——攻击者查不存在的商品 ID,缓存永远 miss。用布隆过滤器在缓存前挡一道:

// 启动时把所有商品ID加载到布隆过滤器
BloomFilter<Long> filter = BloomFilter.create(Funnels.longFunnel(), 1000000, 0.01);
for (Long id : allProductIds) {
    filter.put(id);
}

// 请求进来先过布隆过滤器
public Product getProduct(Long id) {
    if (!filter.mightContain(id)) {
        return Product.notFound();  // 一定不存在,直接返回
    }
    // ... 正常查缓存
}

误判率设 1%,布隆过滤器占内存约 1.2MB(100 万条)。不存在的请求在这一层就被挡住,不会打到数据库。

五、踩坑清单(5 条实际踩过的)

  1. 互斥锁必须设超时(EX 10s),否则拿到锁的请求挂了会死锁,所有请求都卡在等锁。
  2. 没抢到锁的请求别同步等待太久,sleep 200ms 重试,递归次数要有上限。
  3. 热点 key 永不过期要配后台异步刷新,否则缓存和数据库最终会不一致。
  4. 布隆过滤器不支持删除,商品下架时要重建过滤器,别直接删 key。
  5. 大量 key 同时过期要加随机抖动:expire = 3600 + random(0, 300),避免雪崩。
问题类型 防护手段 适用场景
缓存击穿 互斥锁/逻辑过期 单个热点 key
缓存穿透 布隆过滤器/空值缓存 查不存在数据
缓存雪崩 TTL 随机抖动 大量 key 同时过期

这次商品详情页的缓存防护是在乔拓云的商城系统上做的,我主要负责加热点 key 互斥锁、配逻辑过期和接布隆过滤器那几段,改完后活动期间数据库 CPU 稳定在 30% 以下,再没出现打挂的情况。

复盘

  • 缓存击穿是热点 key 过期瞬间并发打 DB,用互斥锁或逻辑过期解决。
  • 布隆过滤器挡穿透,TTL 随机抖动防雪崩,三个问题三种手段。
  • 互斥锁必须设超时,逻辑过期要配异步刷新,否则会引入新问题。

以上是个人实践记录,各平台具体功能以官方实时信息为准。

你们做缓存防护时,热点 key 是用互斥锁还是逻辑过期?

相关文章
|
存储 人工智能 关系型数据库
AI服务器功耗破千瓦,12V供电“扛不住”了?深度解析48V架构下贴片功率电感的新角色
随着AI芯片功耗飙升,12V配电因电流过大、损耗剧增(I²R)难以为继。48V母线成行业新标准,大幅降低损耗、提升密度。中间总线转换器(IBC)作为48V→中间电压的关键枢纽,对高效率、高功率密度提出严苛要求。贴片功率电感由此升级为核心磁件:需兼顾大电流、低DCR、高频低损与紧凑布局,支撑AI服务器供电网络演进。
718 1
AI服务器功耗破千瓦,12V供电“扛不住”了?深度解析48V架构下贴片功率电感的新角色
|
人工智能 数据中心 异构计算
AI服务器电源设计:功率电感选型避坑指南
随着NVIDIA GB300等新一代AI芯片功耗突破千瓦级,GPU供电模块(VRM)正面临“大电流、高频化、高密度”的三重挑战。电感作为储能与滤波的核心元件,其选型直接决定了算力系统的稳定性与能效比。本文结合《AI服务器用一体成型电感》团体标准(2025征求意见稿)及一线大厂实测数据,深度解析AI服务器电感的五大核心指标、避坑指南及主流厂商方案,为硬件工程师提供一份可落地的选型手册。
765 0
AI服务器电源设计:功率电感选型避坑指南
|
23天前
|
Web App开发 安全 应用服务中间件
网站被浏览器报不安全:HTTPS证书链、安全响应头与Mixed Content的排查记录
客户网站突然被浏览器报不安全,排查发现是HTTPS证书链不完整加安全响应头缺失。本文记录了证书链补全、CSP/HSTS等安全响应头配置和Mixed Content修复的完整过程,以及5个实际踩过的坑。
|
2天前
|
Java 关系型数据库 MySQL
导出 10 万行数据直接 OOM:流式查询、游标分页与异步导出任务的落地记录
导出10万行物流记录直接OOM。记录MyBatis流式游标逐行拉取、fetchSize设Integer.MIN_VALUE、异步导出任务+下载链接、SXSSF流式写Excel、临时文件定时清理的完整方案。
|
1月前
|
数据采集 人工智能 缓存
告别AI搜索漏抓:robots.txt与AI爬虫管理的配置实践手记
本文记录AI搜索引擎爬虫管理的完整配置流程:GPTBot/OAI-SearchBot/ClaudeBot等爬虫的识别、robots.txt精确规则写法、Sitemap指令、curl模拟UA验证方法,并附5个真实踩坑案例。
|
7天前
|
人工智能 自然语言处理 监控
从ChatGPT错误数据到Product Schema修复:结构化数据验证的排查流程
AI搜索引擎抓取商品价格错误数据的排查与修复记录:Product Schema结构化数据验证、JSON-LD字段补全、增量同步机制建立的完整流程,含对比表格与踩坑清单,适合电商技术开发者参考。
|
1月前
|
人工智能 自然语言处理 定位技术
告别AI出题重复又超纲:题目去重、难度分级与知识点标注的配置实践
记录AI自动出题配置:题目去重与相似度判重、难度分级与知识点标注、题库冷启动,含5个踩坑案例。
|
5月前
|
存储 Rust NoSQL
一条命令迁移,帮你实现 OpenClaw 与 Hermes Agent 记忆互通!
本文是基于阿里云 Tablestore 的 Agent 记忆共享实战指南:一条命令迁移 OpenClaw 记忆至 Hermes,通过统一 Tablestore 实例、应用 ID 与租户 ID,实现跨Agent(如龙虾与马)记忆自动互通、实时同步与语义检索,支持 CLI 管理与对话中直接调用,安全可靠,开箱即用。
9648 127
|
29天前
|
缓存 监控 搜索推荐
同样的问题别反复调模型:语义缓存的相似度阈值、分层命中与误判兜底
客服问答接口高峰期 P95 四五秒,翻日志发现六成提问是重复或近重复,而简单精确缓存换个说法就 miss。记录语义缓存完整做法:问题归一化加精确层(Redis、TTL 24h)、embedding 向量 topK 检索加余弦相似度阈值(用200对标注样本在0.85和0.95之间校准到0.92)、精确/语义/负缓存三层与 TTL 抖动,以及个性化强时效问题不缓存、点踩反馈收阈值等误命中兜底,附五个踩坑。
|
1月前
|
SQL Java 测试技术
接口变慢先别加机器:一次 P99 抖动的压测定位与连接池调优
从一次 P50 正常、P99 却飙到两三秒的接口抖动说起,讲清为什么先看分位数而不是急着加机器:用 wrk 分档压测配合分阶段埋点,定位到请求在等数据库连接而非算力不足;再记录连接池大小估算、HikariCP 的 maximumPoolSize、connectionTimeout、maxLifetime、keepaliveTime 调参,N+1 慢查询治理,以及线程池有界队列与降级,附瓶颈对比表和五个踩坑。