最有效起点是直接用cache_page装饰器或cache.get_or_set(),但须避开竞态条件和外键穿透:cache_page适用于无用户状态的静态页,类视图需method_decorator包裹;get_or_set是原子操作,避免并发重建;外键字段须一并序列化,更新时用带dispatch_uid的信号清理缓存。

直接用 cache_page 装饰器或 cache.get_or_set() 是最有效、最不容易出错的起点,但必须避开竞态条件和外键穿透这两个高频坑。
cache_page 装饰器怎么用才不踩坑
它适合整页缓存,比如首页轮播图、分类列表这类变化频率低、无用户状态依赖的视图。
- 不要对含
request.user或会话数据的视图直接用cache_page,否则不同用户看到同一缓存内容 -
@cache_page(60 * 15)的超时值建议按数据更新节奏设:静态内容可设几小时,商品库存类建议 ≤ 60 秒 - 如果用了 CDN 或反向代理(如 Nginx),需额外加
Cache-Control头,否则cache_page可能被绕过 - 类视图要用
@method_decorator(cache_page(...))包裹get方法,别写在类定义上
cache.get_or_set() 比 if not cache.get(): cache.set() 安全在哪
后者在高并发下会触发“缓存雪崩式重建”——多个请求同时发现缓存失效,都去查 DB 再写缓存,DB 瞬间被打满。
-
cache.get_or_set('book_list', lambda: list(Book.objects.all()), timeout=300)是原子操作,底层由 Redis/Memcached 保证只有一个线程执行 lambda - 避免传入复杂对象(如 QuerySet):
cache.get_or_set('key', Book.objects.filter(...), ...)会提前求值,失去惰性;应传lambda - 若计算成本极高(如聚合统计),可在 lambda 里加锁(如
cache.add('lock_key', '1', timeout=10))再查库,但优先走get_or_set
外键字段缓存必须一并序列化,不能单独缓存关联对象
比如缓存 Book 对象时只存 book.author_id,后续访问 book.author.name 会触发新 DB 查询,缓存形同虚设。
- 正确做法:缓存前把必要字段展开,
{'id': b.id, 'title': b.title, 'author_name': b.author.name, 'category': b.category.name} - 用
select_related()或prefetch_related()预加载,再转成 dict 缓存,别缓存 ORM 实例本身(可能含未求值关系) - 更新时用信号清理:在
@receiver(post_save, sender=Book)里删cache.delete('book_list'),记得加dispatch_uid='book_save_cleanup'防重复注册
真正难的不是设缓存,而是判断“哪些数据该缓存、缓存多久、失效时怎么清”。一个没被监控的缓存,比没缓存还危险——它会让错误数据静默停留数分钟甚至数小时。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











