物理删除直接触发redis击穿的底层原因是:del命令原子性清空缓存,导致热点key瞬间失效,并发请求全部因get返回null而直击数据库,且无状态标识“正在重建”,形成删一次崩一次的循环。

物理删除直接触发Redis击穿的底层原因
物理删除数据后,对应 key 在 Redis 中被 DEL 命令彻底清除,缓存层立刻变为空白。此时若有并发请求恰好在删除后、新缓存写入前到达,全部会因 GET 返回 null 而直击数据库——这就是击穿。关键在于:物理删除是“原子性清空”,没有过渡缓冲,也不保留任何状态供后续请求识别“正在重建中”。
更危险的是,如果该 key 原本是热点(如商品ID=10086),删除操作常伴随运营下架、库存归零等高并发场景,大量用户刷新页面或重试请求会瞬间涌向数据库,而数据库查不到数据后也无法回填缓存,形成“删一次,崩一次”的循环。
软删除如何切断击穿链路
软删除不删数据,只改状态字段(如数据库加 is_deleted 列,值为 1),并主动更新 Redis 中的 value,把原始业务对象替换成带状态标记的轻量结构。这样既保留 key 存在性,又避免无效查询穿透。
具体操作要点:
- 数据库查询逻辑必须显式过滤
WHERE is_deleted = 0,否则软删形同虚设 - 缓存写入时,对已软删记录仍执行
SET,但 value 是精简结构(如{"status":"deleted","reason":"out_of_stock"}),而非null - 前端或网关层可基于该状态快速返回 410 或友好提示,不再发起重试
- Redis 中该 key 保持原有过期时间(或延长至合理窗口),避免因长期不更新导致自动驱逐
软删除 + 逻辑过期组合防双失效
仅靠软删除还不够:如果软删后 key 恰好过期,或被 LRU 淘汰,仍可能触发击穿。必须叠加逻辑过期机制。
推荐结构:{"data": {...}, "expire_at": 1756923456},其中 expire_at 是服务端计算出的逻辑过期时间戳(非 Redis TTL)。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
读取时判断流程:
-
GET拿到 value → 解析 JSON → 检查expire_at - 若已逻辑过期,且
data.status === "deleted",直接返回软删响应,不查库 - 若已逻辑过期但
data是有效业务数据,则尝试用SETNX获取互斥锁,仅首个成功线程重建缓存 - 未逻辑过期,直接返回
data
这种设计让“删除”和“过期”两个维度解耦:删除是业务动作,过期是缓存策略,彼此不互相触发灾难链。
容易被忽略的软删除陷阱
软删除不是加个字段就万事大吉。最常踩的坑是数据库索引失效和缓存更新时机错位。
比如在 MySQL 中,若原有查询走 PRIMARY KEY,软删后仍能命中;但若业务查询依赖 WHERE category_id = ? AND status = 'on_sale',而没给 (category_id, is_deleted) 建联合索引,软删后每次查询都会全表扫——这等于把击穿从 Redis 转移到了数据库慢查询上。
另一个隐形风险:软删操作本身未同步更新 Redis,而是依赖下游监听 binlog 或 MQ 补偿。一旦补偿延迟或失败,缓存中仍是旧数据,用户看到“已下架商品仍可下单”。务必保证软删 DB 事务与缓存更新在同一本地事务中完成(如用 @Transactional + RedisTemplate.opsForValue().set()),或至少启用强一致的 Canal + RocketMQ 顺序消费。










