关于Django中的缓存,你需要知道的一切

1,004 阅读5分钟

你是否曾想过什么是缓存,是否可以用Django来实现?你想解开Django中缓存的整个概念吗?如果你一直在问这些问题,那么你就很幸运了!在Web开发中,缓存是一个存储数据的层。

在Web开发中,缓存是一个存储数据的层,这些数据经常被取走,因此未来的请求会比从主数据库访问这些数据时更快。通过缓存,你在用容量换取速度。与包含所有数据的主数据库不同,缓存数据只是存储在数据库中的数据的一个子集。

在这篇文章中,我将探讨你可能想要实现缓存的场景,以及在Django应用程序中进行缓存的不同工具和方式。Django中的缓存只需要少量的设置;你只需要告诉Django你想把缓存数据存储在哪里,其余的都会被处理好。让我们开始吧!

前提条件

要学习这篇文章,需要具备以下条件:

  • 有Django和Python的工作经验
  • Python v3.x
  • Django v3.x
  • Redis v5.x

缓存的原因和地点

你应该考虑在你的应用程序中进行缓存的原因如下:

  1. 如果远程服务器发生故障或崩溃,用户仍然可以访问代理上的缓存副本,这可以提高你的应用程序的健壮性。
  2. 缓存减少了访问延迟,因为经常访问的数据是从附近的代理缓存中获取的,而不是数据库本身;因此,数据传输延迟最小。
  3. 缓存减少了服务器基础设施的工作量,因为它将数据广泛地分散到更快的、可替代的数据存储位置。

当决定在你的Django应用程序中哪里进行缓存时,你应该考虑以下一些事情:

  • 哪些视图/模板包含最多的查询?这些将是最好的缓存位置。
  • 哪些模型是被请求最多的?

设置缓存系统

缓存有不同的设置方式,这取决于你想把你的缓存存放在哪里。下面几节将讨论在Django应用程序中设置缓存的方法。

Memcached

这是Django原生支持的最有效的缓存系统。Memcached提供了一个快速的接口来添加、检索和删除缓存中的数据。在这里,所有的数据都直接存储在内存中而不是数据库中,这使得访问数据的速度更快。Memcached像守护进程一样工作,并被分配一些RAM。

要设置它,你只需要首先在本地机器上安装Memcached,然后安装一个Django支持的Python Memcached绑定。Django支持的两种方法是pylibmcpymemcache

在这个例子中,我将使用pymemcache。

要在Django中使用Memcached,请到你的[settings.py](http://settings.py) 文件中,将BACKEND 设为django.core.cache.backends.memcached.PyMemcacheCache ,将LOCATION 设为ip:port 值,其中ip 是Memcached守护程序的IP地址,port 是你希望Memcached运行的端口。

在这个例子中,Memcached运行在localhost(127.0.0.1 )端口9000

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
        'LOCATION': '127.0.0.1:9000',
    }
}

数据库缓存

如果你想在数据库中存储缓存数据,Django有一个用于此目的的后端。

要在数据库中保存缓存数据,你只需要在数据库中创建一个表,方法是进入settings.py 文件,将BACKEND 设置为django.core.cache.backends.db.DatabaseCache ,并将LOCATION 设置为tablename ,这是你要存储缓存的表的名字。

确保用于该表的名称在数据库中没有出现。

接下来,为缓存的数据创建一个数据库表,方法是运行

$ python manage.py createcachetable

文件系统缓存

文件系统缓存包括将缓存数据保存为一个单独的文件。要做到这一点,把BACKEND 设为django.core.cache.backends.filebased.FileBasedCache ,把LOCATION 设为/path/to/django_cache

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': '/path/to/django_cache', 
                   # for windows users: 'c:/path/to/django_cache'
    }
}

本地内存缓存

如果没有指定设置,本地内存缓存是默认的缓存。虽然它的速度几乎和Memcached一样快,但它不能超过单台服务器的规模。因此,它不适合作为使用一个以上Web服务器的应用程序的数据缓存。

本地内存缓存最适合于你的本地开发和测试环境。

缓存LOCATION ,用于识别单个内存存储。要使用它,将BACKEND 设置为django.core.cache.backends.locmem.LocMemCache

下面是一个例子。

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake',
    }
}

启动基础项目

首先,克隆预建的Django项目,其中填充了一些数据,这样我们就可以测试出缓存功能。

当你克隆该项目时,为其创建一个虚拟环境,并运行

$ pip install -r requirements.txt 来安装依赖项。

接下来,运行$ python manage.py runserver ,启动该项目,然后在浏览器上进入http://localhost:8000/recipe,显示项目的内容。

base_project.jpg

你应该看到与上图所示页面类似的内容。注意时间和SQL标签;它们表明你的缓存正在工作。

Django中的缓存级别

Django提供了不同的级别,你可以对Django应用程序进行缓存。这为缓存应用程序提供了细化的功能。在本节中,我将讨论Django提供的级别,并将它们应用于你刚刚克隆的Django项目。这些级别将在下面的章节中讨论。

模板片段缓存

模板片段缓存让你最能控制网站上的缓存内容。模板片段缓存使你可以缓存模板中进行大量计算的特定部分。我们可以通过缓存模板标签{% cache %}

为了使你的模板能够访问这个标签,把{% load cache %} 放在模板的顶部,然后用{% endcache %} 结束。模板标签{% cache %} ,将块的内容缓存一定时间。至少需要两个参数:缓存超时(单位:秒)和指定缓存片段的名称。如果超时时间是None ,模板中的缓存将无限期地持续下去。

例子: 用你喜欢的文本编辑器打开你刚刚克隆的项目,进入app/templates/app/recipes.html,然后插入下面的代码。

{% load cache %}

<html>
<head>
  <title>Recipes</title>
  <style>
      body {
          background-color:yellow;
        }
  </style>
</head>
<body>

{% cache 500 recipe %}


{% for recipe in recipes %}
  <h1>{{ recipe.name }}</h1>
  {% autoescape off %}
    <p>{{ recipe.desc }}</p>
  {% endautoescape %}
  <h2>Ingredients</h2>
  <ul>
    {% for ingredient in recipe.ingredient_set.all %}
    <li>{{ ingredient.description }}</li>
    {% endfor %}
  </ul>

{% endfor %}
{% endcache %}
</body>
</html>

现在,如果你再次运行该项目,你会发现一些变化。如果你仔细观察,你会发现CPU时间和SQL查询的数量都急剧减少。这表明你的网站已经被缓存了,现在运行速度更快。

下面是模板缓存产品的图片:

template_cache.jpg

每个网站的缓存

一旦你的缓存后台设置好了,这是最简单的执行缓存的方法。要实现这一点,你首先需要在你的settings.py文件中添加缓存中间件类。

MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',  #new   
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware', #new
]

接下来,当仍然在你的settings.py文件中,添加以下代码。

CACHE_MIDDLEWARE_ALIAS = 'default'  # The cache alias to use for storage and 'default' is **local-memory cache**.
CACHE_MIDDLEWARE_SECONDS = '600'    # number of seconds before each page is cached
CACHE_MIDDLEWARE_KEY_PREFIX = ''    # This is used when cache is shared across multiple sites that use the same Django instance. You use an empty string if you don’t care for it.

预视图缓存

这里,使用了django.views.decorators.cache.cache_page() 装饰器。Per-view提供了一种更细化的方式,通过缓存单个视图的输出来使用缓存的数据。当进行每视图缓存时,你的视图将看起来像这样。

from django.shortcuts import render
from .models import Recipe
from django.views.decorators.cache import cache_page

@cache_page(600)
def recipes_view(request):
    recipes = Recipe.objects.all()
    return render(request, 'app/recipes.html', {
        'recipes': recipes
    })

cache_page 需要一个参数:缓存超时,单位是秒,本例中是600秒。

与每个站点的缓存不同,通过每个视图的缓存,你可以为网站中真正需要的部分节省内存。如果多个URL指向同一个视图,每个URL将被单独缓存。

低级别的缓存API

低级别的缓存只是意味着你可以选择控制网站上被缓存的内容。例如,你可能在一个网站上有一个视图,其结果取决于几个昂贵的查询,而这些结果在不同的时间间隔内变化。在这里,使用每站点和每视图缓存策略所提供的全页面缓存并不理想,因为你不想缓存整个输出(因为有些数据经常变化)。但是,你还是想缓存那些很少变化的结果。要做到这一点,你可以使用Django的底层API,使用缓存密钥来管理缓存中的单个对象。

在这个例子中,我们仍将使用你刚刚克隆的项目。

view.py文件中,粘贴以下代码。

from django.shortcuts import render
from .models import Recipe
from django.core.cache import cache

def cache_recipes_view(request):
    recipes = cache.get('recipes')
    if recipes is None:
        recipes = Recipe.objects.all()
        cache.set('recipes', recipes)

    return render(request, 'app/recipes.html', {
        'recipes': recipes
    })

然后,更新你的URL,看起来像这样:

from django.contrib import admin
from django.urls import path, include
import debug_toolbar
# from app.views import recipes_view
from app.views import cache_recipes_view #new

urlpatterns = [
    path('admin/', admin.site.urls),
    # path('recipe', recipes_view),
    path('cache_recipe', cache_recipes_view), #new
    path('__debug__/', include(debug_toolbar.urls)),
]

现在,如果你在浏览器上打开http://localhost:8000/cache_recipe,你会看到与原始数据相比,CPU时间和SQL查询都有所减少。

ll_cache.jpg

在Django中使用Redis进行缓存

Redis是一个开源的数据结构存储,可以作为数据库、缓存、消息代理等使用。要开始在你的Django应用程序中使用Redis,你需要首先安装django-redis库。该库使你的Django应用程序更容易连接到Redis。

$ pip install django-redis

接下来,在你的settings.py文件中添加如下代码,就像你对Memcached所做的一样:

CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}

更新你的views.py文件:

from django.shortcuts import render
from .models import Recipe
from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def recipes_view(request):
    recipes = Recipe.objects.all
    return render(request, 'app/recipes.html', {
        'recipes': recipes
    })

接下来,运行你的Django服务器$ python manage.py runserver 。然后,转到另一个终端窗口,运行Redis服务器。你可以用$ redis-cli -n 1 来运行Redis服务器。

这样做之后,你的Django缓存后端将被连接到Redis。

现在,在你的浏览器中进入http://localhost:8000/recipe,你会看到你的网站已经按照预期进行了优化。

redis_cache.jpg

你可以通过进入Redis服务器运行的标签并运行127.0.0.1:6379[1]> keys * ,来检查你的缓存是否由Redis处理;你会看到类似下面的内容。

1) ":1:views.decorators.cache.cache_header..5cf60c9557a12db6b6423fc6f291090e.en-us.UTC"
2) ":1:views.decorators.cache.cache_page..GET.5cf60c9557a12db6b6423fc6f291090e.d41d8cd98f00b204e9800998ecf8427e.en-us.UTC"

复制cache_page的键名,并在get 命令中输入它。

127.0.0.1:6379[1]> get ":1:views.decorators.cache.cache_page..GET.5cf60c9557a12db6b6423fc6f291090e.d41d8cd98f00b204e9800998ecf8427e.en-us.UTC"

当你运行这个命令时,你应该看到渲染的HTML字符串,这表明你的网站在Redis上被缓存了。

总结

这篇文章的目的是让你从对Django应用程序中的缓存一无所知到向你展示在构建Django应用程序时可以通过不同的方式来缓存数据。希望我是成功的。

通过这篇文章,你可以在你的应用程序的任何部分添加缓存。除非你有多余的内存并且不在乎削减成本,否则不建议对整个网站进行缓存。在挑选网站的部分内容进行缓存时,要尽量做一个经过计算的决定。

现在你已经学到了这些,希望你能自信地在你未来的Django项目中或者在你工作的公司期望的时候实施这些知识。