全站缓存需配对使用UpdateCacheMiddleware和FetchFromCacheMiddleware且顺序固定,开发环境必须关闭;cache_page()参数为秒数,仅支持函数视图;底层缓存key须带业务前缀,空值缓存应设短超时防穿透。

全站页面缓存:用 MIDDLEWARE 开关,但别在开发环境硬开
全站缓存本质是把整个 HTTP 响应存下来,后续相同请求直接返回,跳过视图逻辑。Django 用 UpdateCacheMiddleware 和 FetchFromCacheMiddleware 成对工作,必须前后都加进 MIDDLEWARE 列表,顺序不能错——前者在最前,后者在最后。
常见错误是只加一个,或顺序颠倒,结果缓存不生效,还查不出原因。开发时务必关掉,因为静态资源、调试信息、CSRF token 都可能被缓存住,导致表单提交失败或界面错乱。
-
CACHE_MIDDLEWARE_ALIAS指定用哪个 cache backend(比如"default"),不配就默认用"default" -
CACHE_MIDDLEWARE_KEY_PREFIX建议设个字符串(如"site"),避免和其他缓存 key 冲突 - 它依赖响应的
Cache-Control头,所以视图里如果手动写了response['Cache-Control'] = 'no-cache',全站缓存就自动失效
cache_page() 装饰器:按 URL 缓存视图,注意参数单位是秒
cache_page() 是最常用的视图级缓存方式,但它只作用于函数视图;类视图得用 @method_decorator(cache_page(...), name='dispatch')。
关键点是它的参数是「秒数」,不是字符串也不是 timedelta。传 60 * 15 可以,传 "15m" 会报错:TypeError: unsupported operand type(s) for *: 'str' and 'int'。
立即学习“Python免费学习笔记(深入)”;
- 缓存时间写死在装饰器里,适合稳定不变的页面(如帮助页、公告页)
- 如果想动态控制过期时间,得自己调
cache.set(key, value, timeout),cache_page不支持回调或条件判断 - 它默认用
cache_page的 key 前缀,但如果你在同一个 URL 上混用了不同参数的cache_page(60)和cache_page(300),后写的会覆盖前写的——Django 不区分装饰器参数,只看 URL + query string
底层 cache.set() / cache.get():key 命名要带业务上下文,别裸用模型 ID
直接操作缓存 API 最灵活,但也最容易踩坑。最典型问题是 key 冲突:比如两个地方都用 cache.set("user_123", data),但一个存的是用户基本信息,另一个存的是用户权限列表,读的时候就串了。
正确做法是把业务语义塞进 key,比如 "user_profile_123" 和 "user_perms_123"。更稳妥的是用 cache.make_key() 或自己拼接前缀,尤其是多服务共用一个 Redis 实例时。
- value 必须是可序列化的(
str,int,dict,list等),不能是QuerySet或 model 实例——否则序列化失败,缓存写入静默失败 -
cache.get()找不到时返回None,不是抛异常,所以判断要用if result is not None:,而不是if result:(空 dict/list 也会进 false 分支) - Redis 后端下,
timeout=None表示永不过期;但 Memcached 下会当作 0 秒(立刻过期),行为不一致,生产环境统一用具体数字
缓存穿透与击穿:没数据时也得缓存空值,但别缓存太久
当大量请求查一个数据库里根本不存在的 ID(比如恶意刷 /user/999999999),每次都会穿透到 DB。解决方案是:查不到时,也写一条 cache.set("user_999999999", None, 60),让后续请求直接从缓存拿空值。
但这里有个隐形陷阱:如果所有“查无此 ID”的请求都缓存 10 分钟,而其实这个 ID 5 秒后就被创建了,那这 9 分 55 秒的窗口期用户就看不到新数据。
- 空值缓存时间建议设短,比如 1–2 分钟,既防穿透,又不至于卡太久
- 别用
cache.add()替代cache.set()来“防止覆盖”,因为add()在 key 已存在时不写入,空值缓存就失效了 - 更彻底的方案是布隆过滤器(Bloom Filter),但 Django 默认不带,需额外集成,属于进阶场景
缓存策略不是越深越好,而是每层解决明确问题:全站缓存挡流量洪峰,cache_page 简化高频静态页,底层 API 应对复杂查询和空值兜底。真正难的不是怎么写,是怎么在 key 命名、超时设定、空值处理这些细节上保持一致性。










