如何处理空对象缓存导致的空间爆满|Duuu笔记
Redis进阶技巧:本文深入解析
空对象缓存更吃内存因每个空key含40+字节开销及淘汰策略失效;禁用SET key "" EX 60;应采用布隆过滤器预判存在性+短TTL空值缓存。
空对象缓存为什么比想象中更吃内存
直接缓存
null
或空字符串(如
""
、
{}
)本身不占多少空间,但问题出在“量”和“生命周期”上:每个空值都对应一个独立的
key
,而 Redis 的 key 本身有开销(至少 40+ 字节),加上过期时间字段、dictEntry 结构、以及可能的碎片化,10 万个空 key 就可能额外吃掉 5–10MB 内存。更麻烦的是,这些 key 很少被访问,LRU/LFU 淘汰策略几乎不会动它们——因为“没被用过”,所以不会被判定为“最近最少用”或“最不常用”。
别用
SET key "" EX 60
填坑
这是最常见的错误操作:查数据库没结果,就执行
SET user:123 "" EX 60
。它看似解决了穿透,实则埋下三个雷:
空值 key 无法被
allkeys-lru
或
allkeys-lfu
淘汰(除非你主动删)
volatile-lru
虽然能淘汰,但前提是所有空 key 都带了
EX
;一旦漏设,就永久驻留
业务代码里反复判断
if (value == null || value.equals(""))
,逻辑臃肿还易错
真正靠谱的替代方案:布隆过滤器 + 精确过期
布隆过滤器(Bloom Filter)不是用来存数据的,而是快速回答“这个 key 绝对不存在吗?”。它用极小内存(比如 100 万 key 只需 2MB)拦截 99% 的无效查询,让空值根本进不了缓存层。
实操要点:
白瓜AI
白瓜AI,一个免费图文AI创作工具,支持 AI 仿写,图文生成,敏感词检测,图片去水印等等。
下载
初始化阶段,把所有**可能存在的有效 key 前缀或 ID 全集**(如用户 ID、商品 SKU)一次性加载进布隆过滤器(推荐用 RedisBloom 模块或本地 Guava BloomFilter 同步到 Redis)
读取前先查
BF.EXISTS users_filter 123
,返回
0
就直接跳过 Redis 和 DB,不写任何空缓存
只对**确认存在但当前无数据**的场景(如刚注册还没填资料的用户),才写空对象,且必须带短 TTL(如
SETEX user:123:profile "" 30
),并确保该 key 命名可被扫描清理(如加
:profile
后缀)
补救:已经塞了一堆空 key 怎么快速清
别手动
KEYS *
—— 会阻塞 Redis。用安全扫描命令:
redis-cli --scan --pattern "user:*:" | grep -E ':\{\}|"\"\"|:null$' | xargs -r redis-cli DEL
如果不确定哪些是空值,先采样检查:
SCAN 0 MATCH user:* COUNT 100
拿一批 key
对每个 key 执行
GET key
,看返回是否为
(nil)
或空字符串
确认后批量删除,避免单条
DEL
太慢
真正难的不是删,而是下次上线前把布隆过滤器和空值 TTL 策略固化进 DAO 层——否则运维清完,开发一发版又回来。
