本地缓存适用于单进程单机部署、数据不跨请求共享的场景,如函数级中间结果或配置解析对象,典型应用是计算开销大且输入确定的函数(如parse_config),但不适用于需多实例共享的数据。

本地缓存适合什么场景
当你的应用是单进程、单机部署,且缓存数据不跨请求共享(比如函数级中间结果、配置解析后对象),functools.lru_cache 或 cache_tools 这类本地缓存就足够了。它零网络开销、响应快,但只对当前 python 进程有效。
常见错误现象:lru_cache 在 flask/fastapi 的视图函数上用了,结果不同请求间不共享——其实这本来就不该共享,但开发者误以为“缓存生效了”,实际只是同个进程内重复调用时命中。
- 适用:计算开销大、输入确定、生命周期短的函数结果(如
parse_config()、compile_regex()) - 不适用:用户会话状态、实时库存、多实例共享的计数器
-
maxsize设太小会导致频繁淘汰;设None虽无上限,但可能内存泄漏(尤其闭包捕获了大对象)
redis 为什么常被选作分布式缓存
因为它是独立服务、支持多语言客户端、自带过期、原子操作和发布订阅,Python 用 redis-py 接入非常直接。但它不是万能的——网络延迟、序列化开销、连接池配置不当都会拖慢接口。
使用场景:用户登录态(session_id → user_id)、热点文章内容、限流计数器(INCR + EXPIRE)
立即学习“Python免费学习笔记(深入)”;
- 别把大对象(如整个 pandas DataFrame)直塞
set(),先pickle或msgpack序列化,否则 Redis 报错WRONGTYPE Operation against a key holding the wrong kind of value - 连接池必须复用:
redis.Redis(connection_pool=pool),否则每调用一次Redis()就新建 TCP 连接,压测时直接ConnectionRefusedError - 本地开发用
redis://localhost:6379/0,上线务必换成带密码和域名的地址,别留redis://127.0.0.1在配置里
本地 + 分布式缓存组合怎么写才不翻车
典型模式是「先查本地,未命中再查 Redis,回填本地」,但容易忽略一致性问题:Redis 数据更新了,本地副本还 stale 着。
性能影响很实际:加一层本地缓存能减少 30%~70% 的 Redis 请求,但若本地缓存时间设成 5 秒,而业务要求秒级数据新鲜度,那就白搭。
- 推荐用
dogpile.cache,它内置 region + backend 分离,支持backend='redis'和wrap='memory'组合 - 手动实现时,本地缓存 key 必须包含版本号或更新时间戳(如
f"user:{uid}:v2"),避免清不干净 - 不要在异步任务(Celery)里更新本地缓存——它和 Web 进程不共享内存,只会误导自己
什么时候该放弃缓存
如果读写比低于 10:1,或者每次查询本身不到 1ms(比如查 sqlite 里的小表),加缓存反而增加复杂度和故障面。
容易踩的坑:为“看起来高大上”而加 Redis,结果发现 GET user:123 比直接查数据库还慢——原因可能是 Redis 在另一台高延迟机器上,或没开 tcp-nodelay。
- 先用
timeit或 APM 工具确认瓶颈真在数据加载,而不是模板渲染或 json 序列化 - 缓存键设计别嵌套太深:
cache.get(f"report:{year}:{month}:{user.id}")比cache.get(f"report_v2_{hash((year, month, user.id))}")更易 debug - 所有缓存操作必须有 fallback:Redis 不可用时,降级走原始数据源,别让整个接口 500
缓存逻辑越靠近业务代码,就越难测试和替换。把 get_user_cache() 这种胶水函数抽成独立模块,比散落在二十个 view 里好维护得多。