bigywater.tech
Django · 92

Django Cache Framework

cache.set / get 谁都会用,但 timeout=0 是什么意思?incr 遇到不存在的 key 怎么办?存了 None 怎么区分?改了取出的对象会影响缓存吗?这篇把这些边界一个个试出来。

timeout=0
不缓存(立刻 get 得到 None);None 是永不过期;默认 300 秒
incr
对不存在的 key 抛 ValueError,不会自动创建
副本
存取都是 pickle 副本,取出后修改不影响缓存

基本 API

实测 · 本机执行输出
set('a', 1) → get('a') = 1  get("nope") = None  get("nope", "默认") = 默认
add('a', 2)(已存在)返回 False  值仍是 1 ; add("b", 2)(不存在)返回 True
get_or_set('c', lambda: 计算) 第一次: computed  第二次: computed
incr('a') = 2  incr("a", 10) = 12  decr = 11
incr('missing') → ValueError: Key ':1:missing' not found
get_many(['x','y','z']) = {'x': 1, 'y': 2}
delete('x') 返回 True  再 delete 返回 False
has_key('y') = True ; touch('y', 1) = True ; touch 不存在的 = False

本文用 LocMem 后端(本进程内存)演示 API;不同后端在 incr 原子性、序列化格式上可能有差异,见第 93 篇。

过期时间

实测 · 本机执行输出
默认 TIMEOUT = 300 秒(未配置时)
timeout=1 秒,1.1 秒后 get = None
timeout=0 之后立刻 get = None (等于没缓存)
timeout=None get = v (永不过期)
timeout=-1 立刻 get = None
timeout=2 秒,1 秒后 touch(5),又过 1.5 秒(总共 2.5 秒)get = v (touch 重置了过期时间)

timeout 单位是秒。最常见的误用是把「不过期」写成 timeout=0——实际是「不缓存」。

falsy 值

实测 · 本机执行输出
  get('zero') = 0  有这个 key 吗(has_key): True
  get('empty') = ''  有这个 key 吗(has_key): True
  get('none') = None  有这个 key 吗(has_key): True
  get('false') = False  有这个 key 吗(has_key): True
  get('absent') = None  有这个 key 吗(has_key): False
  用哨兵区分: False (存了 None 时仍然返回 None,不是哨兵) True

缓存空结果时常用一个特殊标记值,或者用 has_key(会多一次往返)。

序列化

实测 · 本机执行输出
set 之后又修改原对象,缓存里的仍是: {'items': [1, 2]}
取出后修改,再次取: {'items': [1, 2]}
不能 pickle 的对象 → TypeError: cannot pickle NoPickle

放进缓存的对象要能 pickle。放模型实例会连带它的 _state 一起序列化,取出后是「一份旧快照」。

分步演示:缓存 API

缓存 API
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。LocMemCache;超时测试用真实 sleep
    • 没有验证:incr 在各后端上的原子性、get_or_set 的竞态(第 96、97 篇)、cache.clear() 对共享 Redis 库的危险、cache.delete_many / decr 的其它边界、异步缓存 API(aget 等)
    京ICP备2026067502号-1京公网安备11010502063487号