bigywater.tech
Django · 95

页面缓存与低级缓存

cache_page 怎么决定缓存几份?哪些响应不会被缓存?带登录信息的页面会不会缓存串了?这篇分别试页面缓存、Vary、低级缓存和模板片段缓存,并复现两个容易出事故的情形。

Accept-Language
USE_I18N=True 时不进缓存 key;没有 LocaleMiddleware,zh 与 en 共用一份缓存
Set-Cookie
视图设置 cookie 且没有 Vary: Cookie 的响应会被 cache_page 缓存,cookie 被重放给所有用户
max-age=60
cache_page(60) 同时让响应带 Cache-Control: max-age=60

cache_page

实测 · 本机执行输出
第 1 次: page 1 q=1  第 2 次(同 URL): page 1 q=1  换 ?q=2: page 2 q=2  视图执行次数: 2
响应头 Cache-Control: max-age=60  Expires: True  Vary: None
Cache-Control 里的 max-age 与 cache_page(60) 一致: True

装饰在视图上的整页缓存。key 包含完整 URL(含查询串),所以带跟踪参数的 URL 会各存一份。

Vary

实测 · 本机执行输出
X-Device: phone → device 1 phone | desktop → device 2 desktop | 再 phone → device 1 phone | 视图执行次数: 2 (phone 命中缓存,desktop 是另一份)
Accept-Language: en → lang 1 en | zh → lang 1 en | 视图执行次数: 1 (zh 拿到了 en 的缓存!因为没有 LocaleMiddleware 切换语言)
USE_I18N = True

需要按语言缓存时,要用 LocaleMiddleware 让语言激活;需要按登录用户缓存时,用 vary_on_cookie——更稳妥的做法是,对有用户个性化内容的页面不用整页缓存,改用片段缓存。

不缓存与危险的情形

实测 · 本机执行输出
POST 两次,视图执行次数: 2
500 响应两次,视图执行次数: 2 (错误响应不缓存)
视图 set_cookie 两次,视图执行次数: 1  响应 Vary: None  第二次仍带 Set-Cookie: True

读 Django 6.1.1 的缓存中间件源码:不缓存的情形有——流式响应、状态码不是 200/304、响应设置了 cookie 且 Vary: Cookie、Cache-Control 含 private / no-cache / no-store、Vary: *。视图只是 set_cookie 而没有 Vary: Cookie 时不在此列,所以会被缓存(上面的实测)。

低级缓存

实测 · 本机执行输出
id=1 → computed-1
id=1 → computed-1
id=2 → computed-2
id=1 → computed-1
计算次数: 2
实测 · 本机执行输出
  user 1, counter=1: [计算 1]
  user 1, counter=2(命中缓存): [计算 1]
  user 2, counter=3(另一个 key): [计算 3]
  片段的 key: template.cache.sidebar.6709aa7b0015b3f2257cda58f6ae8ed0

片段缓存的 key 由片段名和额外变量的哈希组成,变量里放用户 id 即可按用户区分。

分步演示:页面缓存与低级缓存

页面缓存
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。LocMemCache;django.test.Client
    • 没有验证:UpdateCacheMiddleware / FetchFromCacheMiddleware 整站缓存、Vary: Cookie 与 Cache-Control: private 时不缓存(只读了源码,没有实测)、cache_control 装饰器、ETag 与缓存的组合(第 23 篇)、CDN 层缓存
    京ICP备2026067502号-1京公网安备11010502063487号