侧边栏壁纸
博主头像
Zeeland

全栈算法工程师 | 大模型创业 | 开源项目分享 | Python开发者 | @Promptulate Founder | @SparkLab cofounder | @LangChainAI Top Contributor | @CogitLab core member

  • 累计撰写 61 篇文章
  • 累计创建 47 个标签
  • 累计收到 7 条评论

目 录CONTENT

文章目录

【django开发手册】DRF自动缓存应用实践分享——缓存注解详解及实现原理

Zeeland
2023-05-09 / 0 评论 / 0 点赞 / 418 阅读 / 1,824 字

前言

Web应用性能是非常重要的,用户访问速度和响应时间会直接影响到用户体验。对于一个需要经常访问、更新的Web应用,缓存机制是非常重要的,它可以避免频繁的数据库访问,提高网站的访问速度、降低服务器负载。本文会探讨以下几个问题:

  • 如何使用DRF自动缓存来优化API性能?以及如何解决常见的缓存问题。
  • django cache_page和 drf cache_response的区别?
  • 缓存的更新机制

Django cache

首先,django也提供了其cache机制,通过from django.views.decorators.cache import cache_page引入cache_page就可以进行使用了,使用方法如下所示:

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request):
    ...

当你第一次调用这个接口的时候,django会为这个接口创建一个60*15秒的缓存,当你后面再去访问的时候,访问速度就会快很多。

需要注意的是,你需要考虑缓存的刷新机制, 一般情况下,缓存不会自动刷新,你需要自己去手动更新,可以说这是一个弊端。

下面的demo是一个将cache_page结合drf的ModelViewSet一起使用,其作用也是接口数据的缓存。

class MeasuringPointViewSet(viewsets.ModelViewSet):
    queryset = models.MeasuringPoint.objects.all().order_by('mpoint_id')
    serializer_class = MeasuringPointSerializer

    @method_decorator(cache_page(60 * 60))
    def list(self, request, *args, **kwargs):
        """根据项目获取所有测点"""
        project_id = request.query_params.get('project_id')
        queryset = self.get_queryset().filter(project_id=project_id)
        serializer = self.get_serializer(results, many=True)
        return paginator.get_paginated_response(serializer.data)

DRF cache

对于上面提到的缓存刷新问题,可以使用DRF自带的缓存方法进行数据更新时的自动清除。

快速上手

  1. 导包,因为drf cache属于其扩展功能,因此需要单独安装drf的扩展库
pip install drf-extensions
  1. views.py 中引入缓存类:
from rest_framework_extensions.cache.decorators import cache_response
  1. 替换@method_decorator(cache_page(60 * 60))@cache_response(key_func='get_cache_key'),并添加get_cache_key函数:
class MeasuringPointViewSet(viewsets.ModelViewSet):
    queryset = models.MeasuringPoint.objects.all().order_by('mpoint_id')
    serializer_class = MeasuringPointSerializer

    @staticmethod
    def get_cache_key(request, *args, **kwargs):
        project_id = request.query_params.get('project_id')
        return f'{request.path}?project_id={project_id}'

    @cache_response(key_func='get_cache_key')
    def list(self, request, *args, **kwargs):
        """根据项目获取所有测点"""
        paginator = CustomPagination()
        project_id = request.query_params.get('project_id')
        queryset = self.get_queryset().filter(project_id=project_id)
        results = paginator.paginate_queryset(queryset, request)
        serializer = self.get_serializer(results, many=True)
        return paginator.get_paginated_response(serializer.data)

这里的get_cache_key函数根据请求的URL和参数构建出缓存的Key值,以避免不同参数的请求缓存混淆。

现在每次数据更新时,DRF会自动清除缓存,确保获取到的数据是最新的。

cache_response和cache_page的区别?

@cache_response@cache_page都是DRF提供的用来缓存视图函数返回结果的装饰器,它们之间的区别在于缓存的级别以及缓存使用的位置不同。

@cache_response缓存的是响应的结果,因此对于同一个请求,如果响应内容不同,那么它们的缓存结果也会不同。而@cache_page则是缓存的整个页面,因此无论响应内容是否相同,对于同一个请求,它们的缓存结果是相同的。

另外,@cache_response是一个更加细粒度的缓存策略,因为它只缓存了响应,这样可以避免将不同的请求的数据混在一起。而@cache_page则是更加粗粒度的缓存策略,由于缓存了整个页面,因此会引发数据混淆的问题,需要对缓存的内容和键做更严格的控制。

因此,一般情况下,我们会优先选择使用@cache_response来缓存响应结果,以避免数据混淆的问题。

什么时候cache_response会更新缓存?缓存时间有多久?还是需要自己手动更新缓存?

@cache_response的缓存持续时间是由DRF的缓存系统设置的,默认情况下,它会将缓存时间设置为None,也就是永久缓存。但是,当使用@cache_response来缓存响应结果时,当以下任意情况出现时,缓存都会自动更新:

  1. 通过PUT、PATCH、POST或DELETE请求更新了资源;
  2. 其他视图调用了与当前视图相同的缓存键(例如,当一个有关联的资源被添加、修改或删除时);
  3. 从缓存中读取的数据已经过期。

这些情况会触发DRF的自动缓存失效机制,它会在从缓存中获取响应结果时,先检查该缓存是否仍然有效,如果已经失效,则会调用视图函数来生成新的响应,并且更新缓存。

需要注意的是,如果你的业务需要,你也可以手动清除缓存。可以使用类似于cache.delete('cache_key')这样的代码来手动删除指定缓存键的缓存。

注意事项

  • 不管你使用cache_page还是cache_response,你都需要注意缓存刷新的问题。
  • 如果你使用了django自带的admin管理系统,当你在后台修改数据的时候,使用cache_response也是无法自动刷新数据的,因为django自带的管理系统是直接修改数据库中的数据,并不会触发cache_response的缓存更新机制,这一点一定要注意一下。所以如果你有精力,对于每一个接口你都可以编写其缓存逻辑,但是更有性价比的方案是,对于那些实在不经常更新的数据,你可以使用缓存,而对于经常更新的数据,你可以把缓存时间设置小一点。

总结

本文主要讨论DRF自动缓存的使用。我们首先介绍了如何使用@cache_response装饰器来缓存视图函数的响应结果。这个装饰器可以提供细粒度的缓存控制,能够避免不同请求的数据混淆问题。我们还介绍了如何通过get_cache_key函数来生成唯一的缓存键,以便于管理和查询。

DRF自动缓存机制是一个非常有用的工具,可以帮助我们优化API性能,提高Web应用的访问速度和响应时间。通过使用@cache_response装饰器和get_cache_key函数,我们可以实现细粒度的缓存控制,避免数据混淆问题。同时,DRF的自动缓存失效机制可以自动清除缓存,避免数据不一致的问题。

0

评论区