geekdoc-python-zh/docs/realpython/cache-in-django-with-redis.md

13 KiB
Raw Permalink Blame History

用 Redis 在 Django 缓存

原文:# t0]https://realython . com/cache-in-django-with-redis/

应用性能对产品的成功至关重要。在一个用户期望网站响应时间少于一秒的环境中,缓慢的应用程序的后果可以用金钱来衡量。即使你不卖任何东西,快速的页面加载也能改善访问你网站的体验。

从收到请求到返回响应,服务器上发生的所有事情都会增加加载页面的时间。根据一般经验,服务器上可以消除的处理越多,应用程序的执行速度就越快。处理完数据后缓存数据,然后在下次请求时从缓存中提供数据,这是减轻服务器压力的一种方式。在本教程中,我们将探索一些阻碍你应用的因素,我们将演示如何用 Redis 实现缓存来抵消它们的影响。

免费奖励: 点击此处获取免费的 Django 学习资源指南(PDF) ,该指南向您展示了构建 Python + Django web 应用程序时要避免的技巧和窍门以及常见的陷阱。

什么是里兹?

Redis 是一个内存中的数据结构存储,可以用作缓存引擎。因为 Redis 将数据保存在 RAM 中所以它可以非常快速地传递数据。Redis 不是我们可以用于缓存的唯一产品。 Memcached 是另一个流行的内存缓存系统,但是很多人都认为 Redis 在大多数情况下优于 Memcached 。就个人而言,我们喜欢为其他目的设置和使用 Redis 的简单性,例如 Redis Queue

Remove ads

开始使用

我们创建了一个示例应用程序,向您介绍缓存的概念。我们的应用程序使用:

安装应用程序

在克隆存储库之前,安装 virtualenvwrapper ,如果你还没有的话。这是一个允许您安装项目所需的特定 Python 依赖项的工具,允许您单独针对应用程序所需的版本和库。

接下来,将目录更改为保存项目的位置,并克隆示例应用程序存储库。完成后,将目录更改为克隆的存储库,然后使用mkvirtualenv命令为示例应用程序创建一个新的虚拟环境:

$ mkvirtualenv django-redis
(django-redis)$

**注意:**用mkvirtualenv创建一个虚拟环境也会激活它。

pip 安装所有需要的 Python 依赖项,然后签出下面的标签:

(django-redis)$ git checkout tags/1

通过构建数据库并用示例数据填充它来完成示例应用程序的设置。确保创建一个超级用户,这样您就可以登录到管理站点。遵循下面的代码示例,然后尝试运行应用程序,以确保它正常工作。访问浏览器中的管理页面,确认数据已正确加载。

(django-redis)$ python manage.py makemigrations cookbook
(django-redis)$ python manage.py migrate
(django-redis)$ python manage.py createsuperuser
(django-redis)$ python manage.py loaddata cookbook/fixtures/cookbook.json
(django-redis)$ python manage.py runserver

运行 Django 应用程序后,继续安装 Redis。

安装 Redis

使用文档中提供的说明下载并安装 Redis 。或者,你可以根据你的操作系统使用包管理器安装 Redis比如 apt-get 或者 homebrew

从新的终端窗口运行 Redis 服务器。

$ redis-server

接下来,在不同的终端窗口中启动 Redis 命令行界面(CLI ),并测试它是否连接到 Redis 服务器。我们将使用 Redis CLI 来检查我们添加到缓存中的键。

$ redis-cli ping
PONG

Redis 提供了一个带有各种命令的 API 开发者可以使用这些命令来操作数据存储。Django 使用 django-redis 在 redis 中执行命令。

在文本编辑器中查看我们的示例应用程序,我们可以在 settings.py 文件中看到 Redis 配置。我们使用内置的 django-redis 缓存作为我们的后端,用CACHES设置定义一个默认缓存。Redis 默认运行在端口 6379 上,我们在设置中指向这个位置。最后要提到的是 django-redis 在键名后面附加一个前缀和一个版本以帮助区分相似的键。在这种情况下我们将前缀定义为“example”。

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

注意:虽然我们已经配置了缓存后端,但是没有一个视图函数实现了缓存。

Remove ads

应用性能

正如我们在本教程开始时提到的,服务器处理请求所做的一切都会减缓应用程序的加载时间。运行业务逻辑和呈现模板的处理开销可能很大。网络延迟会影响查询数据库所需的时间。每当客户端向服务器发送 HTTP 请求时,这些因素就会发挥作用。当用户每秒发起许多请求时,当服务器处理所有请求时,对性能的影响变得很明显。

当我们实现缓存时,我们让服务器处理一次请求,然后将它存储在我们的缓存中。当我们的应用程序收到对同一个 URL 的请求时,服务器从缓存中提取结果,而不是每次都重新处理它们。通常,我们为缓存的结果设置一个生存时间,以便可以定期刷新数据,这是实现的一个重要步骤,以避免提供过时的数据。

当下列情况为真时,您应该考虑缓存请求的结果:

  • 呈现页面涉及大量数据库查询和/或业务逻辑,
  • 您的用户经常访问该页面,
  • 每个用户的数据都是一样的,
  • 并且数据不会经常改变。

从测量绩效开始

首先测试应用程序中每个页面的速度,测试应用程序在收到请求后返回响应的速度。

为了实现这一点,我们将使用 HTTP 负载生成器 loadtest 向每个页面发送大量请求,然后密切关注请求率。访问上面的链接进行安装。安装后,根据/cookbook/ URL 路径测试结果:

$ loadtest -n 100 -k  http://localhost:8000/cookbook/

请注意,我们每秒处理大约 16 个请求:

Requests per second: 16

当我们看到代码在做什么时,我们可以决定如何做出改变来提高性能。应用程序对数据库进行 3 次网络调用,每次请求到/cookbook/,每次调用都需要时间来打开连接和执行查询。在您的浏览器中访问/cookbook/ URL并展开 Django 调试工具栏选项卡来确认这一行为。找到标有“SQL”的菜单并读取查询数:

Django debug toolbar SQL queries

cookbook/services.py

from cookbook.models import Recipe

def get_recipes():
    # Queries 3 tables: cookbook_recipe, cookbook_ingredient,
    # and cookbook_food.
    return list(Recipe.objects.prefetch_related('ingredient_set__food'))

cookbook/views.py

from django.shortcuts import render
from cookbook.services import get_recipes

def recipes_view(request):
    return render(request, 'cookbook/recipes.html', {
        'recipes': get_recipes()
    })

该应用程序还使用一些潜在的昂贵逻辑来呈现模板。

<html>
<head>
  <title>Recipes</title>
</head>
<body>
{% for recipe in recipes %}
  <h1>{{ recipe.name }}</h1>
    <p>{{ recipe.desc }}</p>
  <h2>Ingredients</h2>
  <ul>
    {% for ingredient in recipe.ingredient_set.all %}
    <li>{{ ingredient.desc }}</li>
    {% endfor %}
  </ul>
  <h2>Instructions</h2>
    <p>{{ recipe.instructions }}</p>
{% endfor %}
</body>
</html>

实现缓存

想象一下当用户开始访问我们的站点时,我们的应用程序将发出的网络调用的总数。如果 1000 个用户点击检索食谱的 API那么我们的应用程序将查询数据库 3000 次,每次请求都会呈现一个新的模板。这个数字只会随着我们应用程序的扩展而增长。幸运的是,这个视图非常适合缓存。烹饪书中的食谱很少改变,如果有的话。此外,由于查看食谱是应用程序的中心主题,检索食谱的 API 肯定会被频繁调用。

在下面的例子中我们修改了视图函数来使用缓存。当该函数运行时它检查视图键是否在缓存中。如果键存在那么应用程序从缓存中检索数据并返回它。如果没有Django 查询数据库然后将结果和视图键一起保存在缓存中。第一次运行这个函数时Django 会查询数据库并呈现模板,然后还会对 Redis 进行网络调用,将数据存储在缓存中。对该函数的每个后续调用都将完全绕过数据库和业务逻辑,并查询 Redis 缓存。

example/settings.py

# Cache time to live is 15 minutes.
CACHE_TTL = 60 * 15

cookbook/views.py

from django.conf import settings
from django.core.cache.backends.base import DEFAULT_TIMEOUT
from django.shortcuts import render
from django.views.decorators.cache import cache_page
from cookbook.services import get_recipes

CACHE_TTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)

@cache_page(CACHE_TTL)
def recipes_view(request):
    return render(request, 'cookbook/recipes.html', {
        'recipes': get_recipes()
    })

注意,我们已经向视图函数添加了@cache_page()装饰器,以及生存时间。再次访问/cookbook/ URL 并检查 Django 调试工具栏。我们看到进行了 3 次数据库查询,并对缓存进行了 3 次调用以检查密钥然后保存它。Django 保存了两个键(一个键用于标题,一个键用于呈现的页面内容)。重新加载页面,观察页面活动如何变化。第二次,对数据库进行了 0 次调用,对缓存进行了 2 次调用。我们的页面现在从缓存中提供服务!

当我们重新运行我们的性能测试时,我们看到我们的应用程序加载得更快了。

$ loadtest -n 100 -k  http://localhost:8000/cookbook/

缓存改善了总负载,我们现在每秒处理 21 个请求,比基线多 5 个:

Requests per second: 21

Remove ads

使用命令行界面检查 Redis】

此时,我们可以使用 Redis CLI 来查看 Redis 服务器上存储了什么。在 Redis 命令行中,输入keys *命令该命令返回匹配任何模式的所有键。应该会看到一个名为“example:1:views . decorators . cache . cache _ page”的键。记住“example”是我们的键前缀“1”是版本“views . decorators . cache . cache _ page”是 Django 给键起的名字。复制密钥名并用get命令输入。您应该会看到呈现的 HTML 字符串。

$ redis-cli -n 1
127.0.0.1:6379[1]> keys *
1) "example:1:views.decorators.cache.cache_header"
2) "example:1:views.decorators.cache.cache_page"
127.0.0.1:6379[1]> get "example:1:views.decorators.cache.cache_page"

**注意:**在 Redis CLI 上运行flushall命令,清除数据存储中的所有密钥。然后,您可以再次运行本教程中的步骤,而不必等待缓存过期。

总结

处理 HTTP 请求的成本很高,而且随着应用程序越来越受欢迎,这种成本还会增加。在某些情况下,通过实现缓存,可以大大减少服务器的处理量。本教程介绍了 Django 中使用 Redis 进行缓存的基础知识,但它只是触及了一个复杂主题的表面。

在健壮的应用程序中实现缓存有许多陷阱和问题。控制缓存什么以及缓存多长时间是很困难的。缓存失效是计算机科学中的难题之一。确保私有数据只能由目标用户访问是一个安全问题,在缓存时必须小心处理。

免费奖励: 点击此处获取免费的 Django 学习资源指南(PDF) ,该指南向您展示了构建 Python + Django web 应用程序时要避免的技巧和窍门以及常见的陷阱。

示例应用程序中试验源代码,当您继续使用 Django 开发时,请记住始终将性能放在心上。***