如何在Django中通过Redis Cache安全处理不存在的查询对象?

梦涛同学_4974

梦涛同学_4974

2026-11-15

656人浏览

原创

Django的cache.get()返回None无法区分缓存未命中与数据库查无结果,应使用哨兵值(如_MISSING=object())显式标记“不存在”,并分设合理超时以防止缓存穿透。

如何在django中通过redis cache安全处理不存在的查询对象?

直接说结论:Django 的 cache.get() 返回 None 是正常行为,但“安全处理不存在的查询对象”关键不在缓存层,而在如何区分「缓存未命中」和「数据库查无此对象」——否则会缓存空结果或引发 N+1 查询。

为什么 cache.get() 无法区分「没缓存」和「缓存了 None」?

Django 默认的 cache.get(key) 在 key 不存在或显式存入 None 时都返回 None。如果你在数据库查不到对象后写入 cache.set(key, None),后续就无法判断这次 None 是缓存穿透防护(故意存的),还是单纯还没缓存过。

  • 典型错误模式:obj = cache.get(key); if obj is None: obj = MyModel.objects.get(id=123); cache.set(key, obj) —— 一旦 MyModel.objects.get() 抛 MyModel.DoesNotExist,obj 仍为 None,被误存进缓存,导致永久性缓存穿透
  • 正确做法是捕获异常,并用特殊标记(如 cache.set(key, _MISSING, timeout=...))显式表示「查无此数据」
  • 推荐用哨兵值(例如 _MISSING = object()),避免和业务中可能合法的 None 冲突

用 cache.get_or_set() 要小心它的「原子性假象」

cache.get_or_set(key, default_func, timeout) 看似能省掉条件判断,但它只保证「获取 + 设置」的原子性,不保证 default_func 执行本身是幂等的——尤其当 default_func 是数据库查询时,多个并发请求仍可能同时触发多次 DB 查询。

  • 问题场景:高并发下,key 第一次失效,10 个请求同时调用 cache.get_or_set(..., lambda: MyModel.objects.get(id=123)),全部执行 .get(),造成 DB 压力
  • 缓解方案:对热点 key 加一层本地锁(如 threading.Lock 或 redis.lock),或改用 cache.add() 配合后台异步加载
  • 更稳妥的写法是手动实现「先 get → 检查是否 _MISSING → 否则查 DB → 成功则 set,失败则 set _MISSING」

推荐的健壮缓存读取模式(含 Redis 特性利用)

利用 Redis 的 GET 原语天然返回 nil(对应 Python None),再配合 Django cache backend 的 get_many() 批量探查能力,可减少网络往返。但核心仍是逻辑分层清晰:

Sentinal Redis
Sentinal Redis

监控Redis服务器健康状况、内存、性能及BullMQ队列。检查队列深度、失败任务,分析慢查询并诊断问题。

下载
from django.core.cache import cache
<p>_MISSING = object()  # 全局唯一哨兵</p><p>def get_cached_model_instance(model_class, pk):
cache_key = f"{model_class._meta.db_table}:pk:{pk}"
cached = cache.get(cache_key)
if cached is _MISSING:
return None  # 明确知道 DB 里没有
if cached is not None:
return cached  # 缓存命中</p><h1>缓存未命中:查 DB</h1><pre class="brush:php;toolbar:false;">try:
    obj = model_class.objects.get(pk=pk)
    cache.set(cache_key, obj, timeout=300)
    return obj
except model_class.DoesNotExist:
    cache.set(cache_key, _MISSING, timeout=60)  # 短期缓存「不存在」,防穿透
    return None
  • 注意 _MISSING 的 timeout 应显著短于有效对象的 timeout(比如 60s vs 300s),避免长期阻塞重试
  • 如果用的是 django-redis,可进一步用 cache.client.get_client().exists(cache_key) 判断 key 是否真实存在于 Redis 中(绕过 Django cache 的抽象层),用于调试或监控缓存穿透率
  • 不要把模型实例直接塞进 cache——序列化/反序列化开销大,且可能带循环引用;建议只缓存必要字段字典或使用 model_to_dict()

最易被忽略的一点:缓存「不存在」状态时,timeout 时间必须可控。设太长会导致数据实际已写入 DB 后,缓存还在拒绝响应,形成短暂但可感知的数据不一致;设太短又起不到防穿透作用。这个平衡点得结合你的更新频率和业务容忍度手工调优,没有通用值。

相关专题

更多
Python Web 框架 Django 深度开发
Python Web 框架 Django 深度开发

本专题系统讲解 Python Django 框架的核心功能与进阶开发技巧,包括 Django 项目结构、数据库模型与迁移、视图与模板渲染、表单与认证管理、RESTful API 开发、Django 中间件与缓存优化、部署与性能调优。通过实战案例,帮助学习者掌握 使用 Django 快速构建功能全面的 Web 应用与全栈开发能力。

2026.02.04

613

30

Python Django REST Framework接口安全与认证体系实践
Python Django REST Framework接口安全与认证体系实践

本专题围绕 Django REST Framework 展开,深入讲解 API 认证、JWT 鉴权、权限控制、接口安全防护以及防攻击策略设计。通过完整后端案例,帮助开发者构建安全可靠的 Web API 服务体系。

2026.06.29

280

15

常用的数据库软件
常用的数据库软件

常用的数据库软件有MySQL、Oracle、SQL Server、PostgreSQL、MongoDB、Redis、Cassandra、Hadoop、Spark和Amazon DynamoDB。更多关于数据库软件的内容详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.02

4969

19

内存数据库有哪些
内存数据库有哪些

内存数据库有Redis、Memcached、Apache Ignite、VoltDB、TimesTen、H2 Database、Aerospike、Oracle TimesTen In-Memory Database、SAP HANA和ache Cassandra。更多关于内存数据库相关问题,详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2023.11.14

4555

11

mongodb和redis哪个读取速度快
mongodb和redis哪个读取速度快

redis 的读取速度比 mongodb 更快。原因包括:1. redis 使用简单的键值存储,而 mongodb 存储 json 格式的数据,需要解析和反序列化。2. redis 使用哈希表快速查找数据,而 mongodb 使用 b-tree 索引。因此,redis 在需要高性能读取操作的应用程序中是一个更好的选择。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.02

8852

6

redis怎么做缓存服务器
redis怎么做缓存服务器

redis 作为缓存服务器的答案:redis 是一款开源、高性能、分布式的键值存储,可作为缓存服务器使用。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

683

6

redis怎么解决数据一致性
redis怎么解决数据一致性

redis 提供了两种一致性模型,以维护副本数据一致性:强一致性 (sync) 确保写操作仅在复制到所有从节点后才完成;最终一致性 (async) 则在主节点上写操作后认为已完成,牺牲一致性换取性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

776

6

mysql和redis怎么保证双写一致性
mysql和redis怎么保证双写一致性

确保 mysql 和 redis 双写一致性的技术包括:1、事务性更新:同时更新 mysql 和 redis,保证一致性;2、主从复制:mysql 主服务器更改同步到 redis 从服务器;3、基于事件的更新:mysql 记录更改并发送到 redis等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

9262

6

redis缓存一般存些什么数据
redis缓存一般存些什么数据

redis缓存中存储的数据类型包括:字符串、哈希、列表、集合、有序集合、位图、地理空间数据和hyperloglog。这些数据类型适用于存储各种数据,从简单信息到复杂对象和地理位置。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.04.07

1200

6

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go-Zero官方文档
Go-Zero官方文档

共0课时 | 0人学习