缓存失效时优先用 delete 后异步 set,而非 set 空值或直接覆盖;因 delete 可避免脏读、降低并发回源风险,且空缓存可接受时更安全。

缓存失效时该用 delete 还是 set 空值?
缓存更新不是“写新值”就完事,关键是让旧值真正不可见。直接 set 新值看似简单,但若写入失败或部分成功,旧缓存仍残留;而先 delete 再 set,能避免脏读,但会引发缓存穿透风险。
- 如果业务允许短暂空缓存(比如商品详情页可接受一次 DB 查询),优先
delete后异步set,而不是覆盖写 - 对高并发读+低频更新的场景(如用户配置),
delete更安全:避免多个线程同时发现缓存 miss、重复回源 - 别用
set(key, None)模拟删除——很多缓存后端(如 Redis)对空值不自动过期,且应用层需额外判空逻辑
用 @lru_cache 做实例方法缓存?别踩这个引用泄漏坑
@lru_cache 绑定的是函数对象,不是调用上下文。给实例方法加它,相当于把整个 self 实例作为参数缓存键的一部分,极易导致对象无法被 GC 回收。
- 实例方法要缓存,改用
functools.cached_property(Python 3.8+),它按实例绑定,生命周期与对象一致 - 或手动在实例上维护字典:
self._cache = {},自己控制键生成和清理 -
@lru_cache(maxsize=None)在类方法中可用,但必须确保所有参数都是可哈希的(比如不能传dict或list)
Redis 缓存更新选 SET 还是 GETSET?看原子性需求
GETSET 看似能“取旧值+设新值”一步到位,但实际很少需要旧值;而 SET 配合 EX 参数更轻量、更可控。
- 大多数场景直接
redis.set(key, new_value, ex=300)就够了,简洁且支持过期时间 - 只有当你要基于旧值做条件判断(比如“仅当旧值为 A 时才更新为 B”),才考虑
redis.eval写 Lua 脚本,而不是依赖GETSET - 注意:Python 的
redis-py默认不启用连接池复用,高频SET下记得配ConnectionPool,否则容易耗尽 socket
缓存版本号该放 key 里还是 value 里?
版本号放 key 里(如 f"user:{uid}:v2")最干脆,更新时直接切 key,老 key 自然淘汰;放 value 里(如 {"v": 2, "data": ...})要每次反序列化判断,还可能漏升级。
立即学习“Python免费学习笔记(深入)”;
- key 版本适合全量更新场景(如用户资料结构大改),运维可直接
KEYS user:*:v1批量清理 - value 版本只在极少数情况有用:比如你无法控制缓存写入方(第三方 SDK),只能靠读时兼容多版本数据
- 别用时间戳当版本号(如
v20240501),它隐含“所有更新必须同步上线”的假设,微服务部署不同步时会出乱子
缓存策略最难的从来不是怎么写代码,而是想清楚「谁负责决定缓存是否有效」——是写操作方主动失效,还是读操作方被动校验。这个责任边界一旦模糊,问题就会藏在凌晨三点的慢查询日志里。










