Django框架认知:自带强大的缓存系统
动态网站的一个基本取舍是,它们是动态的。每当用户请求一个页面时,网络服务器都会进行各种计算 —— 从数据库查询到模板渲染再到业务逻辑 —— 以创建访客看到的页面。从处理花费的角度来看,这比标准的从文件系统中读取文件的服务器安排要昂贵得多。对于中高流量的网站来说,必须尽可能地减少开销。这就是缓存的用武之地。
缓存就是将昂贵的计算结果保存下来,这样下次就不用再进行计算了。以下是一些伪代码,解释了如何在动态生成的网页中使用这种方法:
given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page
Django 自带强大的缓存系统,可以让动态页面不必为每次请求计算。为了方便,Django 提供了不同级别的缓存粒度。可以缓存特定视图,只缓存难以生成的部分,或者缓存整个网站。
设置缓存
缓存系统需要少量的设置。也就是说,你必须告诉它你的缓存数据应该放在哪里 —— 是在数据库中,还是在文件系统上,或者直接放在内存中。这是一个重要的决定,会影响你的缓存的性能;是的,有些缓存类型比其他类型快。
缓存设置项位于你的配置文件的缓存配置中。这里有缓存配置所有可用值的说明。
Memcached
Memcached 是一个完全基于内存的缓存服务器,是 Django 原生支持的最快、最高效的缓存类型,最初被开发出来用于处理 LiveJournal.com 的高负载,随后由 Danga Interactive 开源。Facebook 和 Wikipedia 等网站使用它来减少数据库访问并显著提高网站性能。
Memcached 以一个守护进程的形式运行,并且被分配了指定数量的 RAM。它所做的就是提供一个快速接口用于在缓存中添加,检索和删除数据。所有数据都直接存储在内存中,因此不会产生数据库或文件系统使用的开销。
在安装了 Memcached 本身之后,你需要安装一个 Memcached 绑定。有几个 Python Memcached 绑定可用;Django 支持的两个绑定是 pylibmc 和 pymemcache 。
在 Django 中使用 Memcached :
设置BACKEND
为 django.core.cache.backends.memcached.PyMemcacheCache 或 django.core.cache.backends.memcached.PyLibMCCache (取决于你选择的 memcached 绑定)。- 将
LOCATION
设置为ip:port
值,其中ip
是 Memcached 守护进程的 IP 地址,port
是 Memcached 运行的端口,或者设置为unix:path
值,其中path
是 Memcached Unix socket 文件的路径。
Memcached 运行在 localhost(127.0.0.1)端口 11211,使用 pymemcache
绑定:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': '127.0.0.1:11211',
}
}
Memcached 可以通过本地 Unix 套接字文件 /tmp/memcached.sock
使用 pymemcache
绑定:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}
Memcached 有一个很好的特性,就是它可以在多台服务器上共享一个缓存。这意味着你可以在多台机器上运行 Memcached 守护进程,程序将把这组机器作为一个 单一 的缓存,而不需要在每台机器上重复缓存值。要利用这个特性,请在 LOCATION
中包含所有服务器地址,可以是分号或逗号分隔的字符串,也可以是一个列表。
缓存是通过运行在 IP 地址 172.19.26.240 和 172.19.26.242 上的 Memcached 实例共享的,这两个实例都在 11211 端口上:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
缓存是由运行在 IP 地址 172.19.26.240(端口11211)、172.19.26.242(端口11212)和 172.19.26.244(端口11213)上的 Memcached 实例共享的:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11212',
'172.19.26.244:11213',
]
}
}
基于内存的缓存有一个缺点:因为缓存的数据存储在内存中,如果服务器崩溃数据将丢失。内存并不是用来永久存储数据的,所以不要依赖基于内存的缓存作为唯一的数据存储。
Django 3.2增加了 PyMemcacheCache
后端。
3.2 版后已移除MemcachedCache`后端已被废弃,因为 python-memcached
有一些问题,而且似乎没有维护。使用 PyMemcacheCache
或 PyLibMCCache
代替。
数据库缓存
Django 可以在数据库中存储缓存数据。如果你有一个快速、索引正常的数据库服务器,这种缓存效果最好。
用数据库表作为你的缓存后端:
- 将
BACKEND
设置为django.core.cache.backends.db.DatabaseCache
- 将
LOCATION
设置为数据库表的tablename
。这个表名可以是没有使用过的任何符合要求的名称。
在这个例子中,缓存表的名称是 my_cache_table
:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
}
}
创建缓存表¶
使用数据库缓存之前,必须通过命令创建缓存表:
python manage.py createcachetable
该表的格式与 Django 数据库缓存系统期望的一致,表名为LOCATION
。
如果使用多个数据库缓存,createcachetable
会为每个缓存创建一个表。如果使用多个数据库, createcachetable
观察数据库路由器的 allow_migrate()
方法。像 migrate
一样, createcachetable
不会影响已经存在的表,它只创建缺失的表。
多数据库
如果在多数据库中使用缓存,需要设置数据库缓存表的路由指令。数据库缓存表在 django_cache
应用程序中显示为 CacheEntry
的模型名。
比如,下面的路由可以将所有缓存读取操作指向 cache_replica
,并且所有的写操作指向 cache_primary
。缓存表将会只同步到 cache_primary
。
class CacheRouter:
"""A router to control all database cache operations"""
def db_for_read(self, model, **hints):
"All cache read operations go to the replica"
if model._meta.app_label == 'django_cache':
return 'cache_replica'
return None
def db_for_write(self, model, **hints):
"All cache write operations go to primary"
if model._meta.app_label == 'django_cache':
return 'cache_primary'
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"Only install the cache model on primary"
if app_label == 'django_cache':
return db == 'cache_primary'
return None
如果没有指定路由指向数据库缓存模型,缓存后端将使用默认
的数据库。
文件系统缓存
基于文件的后端序列化并保存每个缓存值作为文件。可将 BACKEND
设置为 "django.core.cache.backends.filebased.FileBasedCache"
并将 LOCATION
设置为路径。比如,在 /var/tmp/django_cache
存储缓存数据,使用以下配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}
如果使用 Windows 系统,将驱动器号放在路径开头,如下:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': 'c:/foo/bar',
}
}
目录路径是绝对路径。确保配置指向的目录存在并且可由运行 Web 服务器的系统用户读写,或者可以直接由运行 Web 服务器的系统用户创建。
本地内存缓存
如果配置文件中没有指定其他缓存,这是默认缓存。可将 BACKEND
设置为 "django.core.cache.backends.locmem.LocMemCache"
。例如:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
LOCATION
被用于标识各个内存存储。如果只有一个 locmem
缓存,可以忽略 LOCATION
如果有多个本地内存缓存,那么至少要为其中一个起个名字,以便将它们区分开。
使用自定义缓存后端
使用 Python 导入路径作为 BACKEND
的 CACHES
配置中的 BACKEND
,像这样:
CACHES = {
'default': {
'BACKEND': 'path.to.backend',
}
}
缓存参数
每个缓存后端可以通过额外的参数来控制缓存行为。这些参数在 CACHES
配置中作为附加键提供。有效参数如下:
TIMEOUT
:缓存的默认超时时间,以秒为单位。这个参数默认为300
秒(5 分钟)。你可以将TIMEOUT
设置为None
,这样,默认情况下,缓存键永远不会过期。值为0
会导致键立即过期(实际上是 “不缓存”)。OPTIONS
:任何应该传递给缓存后端的选项。有效的选项列表会随着每个后端而变化,由第三方库支持的缓存后端会直接将其选项传递给底层缓存库。实施自有缓存策略的缓存后端(即
locmem
、filesystem
和database
后端)将尊重以下选项:Memcached 后端将
OPTIONS
的内容作为关键字参数传递给客户端构造函数,允许对客户端行为进行更高级的控制。具体用法请看下文。MAX_ENTRIES
:删除旧值之前允许缓存的最大条目。默认是300
。CULL_FREQUENCY
:当达到MAX_ENTRIES
时,被删除的条目的比例。实际比例是1 / CULL_FREQUENCY
,所以将CULL_FREQUENCY
设置为2
,即当达到MAX_ENTRIES
时将删除一半的条目。这个参数应该是一个整数,默认为3
。CULL_FREQUENCY
的值为0
意味着当达到MAX_ENTRIES
时,整个缓存将被转储。在某些后端(特别是database
),这使得缓存速度 更 快,但代价是缓存未命中更多。
KEY_PREFIX
。一个自动包含在 Django 服务器使用的所有缓存键中的字符串(默认为前缀)。查看 缓存文档 获取更多信息。
VERSION
:Django 服务器生成的缓存键的默认版本号。查看 缓存文档 获取更多信息。
KEY_FUNCTION
一个字符串,包含一个函数的点分隔路径,该函数定义了如何将前缀、版本和键组成一个最终的缓存键。查看 缓存文档 获取更多信息。
配置一个文件系统后端,超时为 60 秒,最大容量 1000 项:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
'TIMEOUT': 60,
'OPTIONS': {
'MAX_ENTRIES': 1000
}
}
}
站点缓存
一旦缓存设置完毕,使用缓存最简便的方式是缓存整个站点。在 MIDDLEWARE
中添加 'django.middleware.cache.UpdateCacheMiddleware'
和 'django.middleware.cache.FetchFromCacheMiddleware'
,如下:
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]
最后,在 Django 设置文件里添加下面的必需配置:
CACHE_MIDDLEWARE_ALIAS
-- 用于存储的缓存别名。CACHE_MIDDLEWARE_SECONDS
-- 应缓存每个页面的秒数。CACHE_MIDDLEWARE_KEY_PREFIX
-- 如果使用相同的 Django installation ,通过多站点进行缓存共享,请将此值设置为站点名,或者设置成在Django 实例中唯一的其他字符串,以此防止键冲突。如果你不介意,可以设置成空字符串。
在请求和响应标头允许的情况下,FetchFromCacheMiddleware
缓存状态为200的 GET 和 HEAD 响应。对于具有不同查询参数的相同URL的请求的响应被认为是单独的页面,并分别缓存。这个中间件期望一个HEAD请求的响应头与相应的GET请求具有相同的响应头;在这种情况下,它可以为HEAD请求返回一个缓存的GET响应。
view视图缓存
django.views.decorators.cache.
cache_page
()¶
使用缓存框架的通用办法是缓存视图结果。django.views.decorators.cache
定义了一个 cache_page
装饰器,它将自动缓存视图的响应:
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...
cache_page
使用了一个单独的参数:缓存过期时间,以秒为单位。在上面的例子里,my_view()
视图的结果将缓存15分钟。(注意,我们用 60 * 15
这样的方式编写,目的是方便阅读。 60 * 15
将计算为 900
,也就是15分钟乘以每分钟60秒。)
cache_page
设置的缓存超时优先于 Cache-Control
头中的 ``max-age'' 指令。
和缓存站点一样,对视图缓存,以 URL 为键。如果许多 URL 指向相同的视图,每个 URL 将被单独缓存。继续以 my_view
为例,如果你的 URLconf 是这样的:
urlpatterns = [
path('foo/<int:code>/', my_view),
]
那么 /foo/1/
和 /foo/23/
的请求将被分别缓存,正如你所料。但一旦部分 URL (比如 /foo/23/
)已经被请求,那么随后的请求都将使用缓存。
cache_page
也可以传递可选关键字参数 cache
,它指引装饰器在缓存视图结果时使用特定的缓存(来自 CACHES
设置)。默认情况下,将使用默认缓存,但你可以指定任何你想要的缓存:
@cache_page(60 * 15, cache="special_cache")
def my_view(request):
...
你可以基于每个视图覆盖缓存前缀。cache_page
传递了一个可选关键字参数 key_prefix
,它的工作方式与中间件的 CACHE_MIDDLEWARE_KEY_PREFIX
相同。可以这样使用它:
@cache_page(60 * 15, key_prefix="site1")
def my_view(request):
...
key_prefix
和 cache
参数可能需要被一起指定。key_prefix
参数和 CACHES
下指定的 KEY_PREFIX
将被连接起来。
此外, cache_page
在响应中自动设置 Cache-Control
和 Expires
头, 这会影响 下游缓存.
在 URLconf 中指定视图缓存
在 URLconf 中使用 cache_page
时,可以这样包装视图函数。如
urlpatterns = [
path('foo/<int:code>/', my_view),
]
将 my_view
包含在 cache_page
中:
from django.views.decorators.cache import cache_page
urlpatterns = [
path('foo/<int:code>/', cache_page(60 * 15)(my_view)),
]
模板片段缓存
使用 cache
模板标签(tag)来缓存模板片段。要使模板能够访问这个标签,请将 {% load cache %}
放在模板顶部。
{% cache %}
模板标签在给定的时间里缓存片段内容。它需要至少两个参数:缓存时效时间(以秒为单位),缓存片段的名称。如果缓存失效时间被设置为 None
,那么片段将被永久缓存。名称不能使变量名。例如:
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
缓存失效时间可以是模板变量,只要模板变量解析为一个整数值即可。例如,模板变量 my_timeout
被设置成 600
,那么下面两个例子是一样的:
{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}
避免在模板中重复,可以在某处设置缓存失效时间,然后复用这个值。
默认情况下,缓存标签会先尝试使用名为 "template_fragments" 的缓存。如果这个缓存不存在,它将回退使用默认缓存。你可以选择一个备用缓存后端与 using
关键字参数一起使用,这个参数必须是标签的最后一个参数。
{% cache 300 local-thing ... using="localcache" %}