如何为Django REST Framework的retrieve接口实现持久化缓存
问题根因
当前缓存方案不生效由两个Django默认配置的设计导致:
FileBasedCache默认最大缓存条目数MAX_ENTRIES为300,超出阈值后会自动淘汰旧缓存。你需要缓存200万条记录,新写入的缓存会持续触发淘汰逻辑,因此缓存目录始终维持极小体积,每次请求都会改写目录内容。cache_page装饰器默认将Cookie、认证信息、Accept编码等请求头纳入缓存key计算,如果接口开启鉴权,不同用户访问同一条数据会生成独立缓存副本,既浪费存储空间,又会进一步加快缓存淘汰速度,无法命中公共缓存。
可行解决方案
方案1:修正现有cache_page配置(快速修复)
调整缓存后端参数,同时收紧缓存key生成规则,让同一条记录的缓存可以跨请求共享:
- 修改
settings.py中的缓存配置:
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/home/django_cache', # 最大缓存条目数略大于总记录数,避免频繁触发淘汰 'MAX_ENTRIES': 2200000, # 缓存满后一次性删除10%的过期/旧缓存,降低磁盘IO频率 'CULL_FREQUENCY': 10, 'TIMEOUT': 60 * 60 * 24 * 365, } }
- 修改视图类的
retrieve方法,清空不必要的Vary规则:
from django.utils.decorators import method_decorator from django.views.decorators.cache import cache_page from django.views.decorators.vary import vary_on_headers from rest_framework import mixins, viewsets, response from .models import Annotation from .serializers import AnnotationSerializer class AnnotationViewSet( mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet ): queryset = Annotation.objects.all() serializer_class = AnnotationSerializer @method_decorator(cache_page(60 * 60 * 24 * 365, key_prefix="annotation_retrieve")) @method_decorator(vary_on_headers()) def retrieve(self, request, *args, **kwargs): instance = self.get_object() serializer = self.get_serializer(instance) return response.Response(serializer.data)
注意:如果接口返回内容会根据登录用户身份变化,不要清空Vary规则,直接使用方案2。
方案2:使用低层缓存API(更推荐,粒度精准)
cache_page是为通用页面缓存设计的,针对单资源接口,直接调用Django低层缓存API手动控制读写逻辑更灵活,缓存key直接和记录ID绑定,完全符合单条记录对应独立缓存的需求,也不会受请求头干扰:
- 保留方案1中调整过
MAX_ENTRIES参数的CACHES配置 - 重写视图逻辑,增加缓存读写、更新/删除时主动失效缓存的逻辑:
from django.core.cache import cache from rest_framework import mixins, viewsets, response from .models import Annotation from .serializers import AnnotationSerializer class AnnotationViewSet( mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet ): queryset = Annotation.objects.all() serializer_class = AnnotationSerializer def retrieve(self, request, *args, **kwargs): # 缓存key直接绑定记录ID,全局唯一 cache_key = f"annotation_detail:{kwargs['pk']}" cached_data = cache.get(cache_key) if cached_data: return response.Response(cached_data) # 缓存未命中时查询数据库 instance = self.get_object() serializer = self.get_serializer(instance) data = serializer.data cache.set(cache_key, data, timeout=60*60*24*365) return response.Response(data) def perform_update(self, serializer): instance = serializer.save() # 数据更新时删除对应缓存,避免返回旧内容 cache.delete(f"annotation_detail:{instance.pk}") def perform_destroy(self, instance): # 数据删除时同步清理缓存 cache.delete(f"annotation_detail:{instance.pk}") instance.delete()
额外注意事项
- 提前确认Django运行进程对
/home/django_cache目录有读写权限,否则缓存写入会静默失败。 - 文件缓存适合读多写少的冷数据场景,如果后续访问量提升,建议替换为开启持久化的Redis缓存,读写性能比磁盘文件高几个数量级。
- 按单条序列化结果1KB估算,200万条缓存总磁盘占用约2GB,部署前请预留足够存储空间。
内容的提问来源于stack exchange,提问作者user1315621
相关产品推荐
相关产品推荐

