django cache 缓存框架
动态网站的基本权衡是,它们是动态的。每次用户请求页面时,Web服务器都会进行各种计算 - 从数据库查询到模板呈现再到业务逻辑 - 以创建站点访问者看到的页面。从处理开销的角度来看,这比标准的文件读取文件系统服务器安排要昂贵得多
对于大多数Web应用程序来说,这种开销并不是什么大问题,但对于中到高流量的站点,尽可能减少开销是至关重要的。这就需要用到缓存了。
这里有一些伪代码解释了这对于动态生成的Web页面是如何工作的:
#给定一个URL,尝试在缓存中找到该页面
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
设置缓存
内存缓存(Memcached)
内存缓存(Memcached):Memcached是Django原生支持的最快,最有效的缓存类型,是一个完全基于内存的缓存服务器,最初是为了处理LiveJournal.com的高负载而开发的,后来由Danga Interactive开源。 Facebook和Wikipedia等网站使用它来减少数据库访问并显着提高网站性能。
Memcached作为守护进程运行,并分配了指定数量的RAM。 它所做的就是提供一个快速接口,用于在缓存中添加,检索和删除数据。 所有数据都直接存储在内存中,因此不会产生数据库或文件系统使用的开销。
安装Memcached本身后,您需要安装Memcached绑定。 有几个Python Memcached绑定可用; 最常见的两个是python-memcached和pylibmc。
在Django中使用Memcached:
1.将BACKEND设置为django.core.cache.backends.memcached.MemcachedCache或 django.core.cache.backends.memcached.PyLibMCCache(取决于您选择的memcached绑定)
2. 将LOCATION设置为ip:port值,其中ip是Memcached守护程序的ip地址,port是Memcached正在运行的端口,或者设置为unix:path值,其中path是Memcached Unix套接字文件的路径
在此示例中,Memcached使用python-memcached绑定在localhost(127.0.0.1)端口11211上运行:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
在此示例中,Memcached可通过本地Unix套接字文件/tmp/memcached.sock使用python-memcached绑定获得:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}
使用pylibmc绑定时,不要包含unix:/前缀:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '/tmp/memcached.sock',
}
}
Memcached的一个出色功能是它能够在多个服务器上共享缓存。 这意味着您可以在多台计算机上运行Memcached守护程序,程序会将该组计算机视为单个缓存,而无需在每台计算机上复制缓存值。 要利用此功能,请在LOCATION中包括所有服务器地址,可以是分号或逗号分隔的字符串,也可以是列表
在此示例中,缓存通过端口11211上的IP地址172.19.26.240和172.19.26.242上运行的Memcached实例共享:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'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.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11212',
'172.19.26.244:11213',
]
}
}
关于Memcached的最后一点是基于内存的缓存有一个缺点:因为缓存的数据存储在内存中,如果服务器崩溃,数据将会丢失。 显然,内存不适用于永久数据存储,因此不要依赖基于内存的缓存作为唯一的数据存储。 毫无疑问,没有任何Django缓存后端应该用于永久存储 - 它们都是用于缓存而不是存储的解决方案 - 但我们在此指出这一点,因为基于内存的缓存特别是暂时的。
数据库缓存
Django可以将其缓存的数据存储在您的数据库中。 如果你有一个快速,索引良好的数据库服务器,这种方法效果最好。
使用数据库表作为缓存后端:
1.将BACKEND设置为django.core.cache.backends.db.DatabaseCache
2.将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不会触及现有表。它只会创建缺少的表。
要打印将要运行的SQL,而不是运行它,请使用createcachetable - dry-run选项
多个数据库
如果对多个数据库使用数据库缓存,则还需要为数据库缓存表设置路由指令。 出于路由的目的,数据库缓存表在名为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服务器的系统用户可读写。 继续上面的示例,如果您的服务器作为用户apache运行,请确保目录/ var / tmp / django_cache存在且用户apache是可读写的。
本地内存缓存
如果未在设置文件中指定其他缓存,则这是默认缓存。 如果您想要内存缓存的速度优势但不具备运行Memcached的能力,请考虑本地内存缓存后端。 此缓存是按进程(见下文)和线程安全的。 要使用它,请将BACKEND设置为“django.core.cache.backends.locmem.LocMemCache”。 例如:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
缓存LOCATION用于标识各个内存存储。 如果您只有一个locmem缓存,则可以省略LOCATION; 但是,如果您有多个本地内存缓存,则需要为其中至少一个分配一个名称,以便将它们分开。
缓存使用最近最少使用(LRU)的剔除策略。
请注意,每个进程都有自己的私有缓存实例,这意味着不可能进行跨进程缓存。 这显然也意味着本地内存缓存不是特别节省内存,因此它可能不是生产环境的好选择。 它对开发很有好处。
虚拟缓存(用于开发)
最后,Django带有一个“虚拟”缓存,实际上并不缓存 - 它只是实现缓存接口而不做任何事情。
如果您的生产站点在不同的地方使用重型缓存,但在开发/测试环境中您不想缓存并且不希望将代码更改为后者的特殊情况,这将非常有用。 要激活虚拟缓存,请像这样设置BACKEND:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
使用自定义缓存后端
虽然Django支持开箱即用的许多缓存后端,但有时您可能希望使用自定义缓存后端。 要在Django中使用外部缓存后端,请使用Python导入路径作为CACHES设置的BACKEND,如下所示:
CACHES = {
'default': {
'BACKEND': 'path.to.backend',
}
}
如果您正在构建自己的后端,可以使用标准缓存后端作为参考实现。您将在django源代码的django/core/cache/backends/目录中找到代码。
注意:如果没有真正令人信服的理由,例如主机不支持它们,您应该坚持使用Django附带的缓存后端。 它们经过了良好的测试,易于使用。
缓存参数
每个缓存后端都可以有额外的参数来控制缓存行为。这些参数在CACHES设置中作为附加键提供。有效参数如下:
1.TIMEOUT:用于缓存的默认超时(以秒为单位)。 此参数默认为300秒(5分钟)。 您可以将TIMEOUT设置为None,以便默认情况下缓存键永不过期。 值为0会导致密钥立即过期(实际上“不缓存”)。
2.OPTIONS:应传递给缓存后端的任何选项。 有效选项列表将随每个后端而变化,由第三方库支持的缓存后端将直接将其选项传递给基础缓存库。
缓存后端,实现自己的筛选策略(即locmem,文件系统和数据库后端)将遵循以下选项:
1.MAX_ENTRIES:删除旧值之前缓存中允许的最大条目数。 此参数默认为300。
2.CULL_FREQUENCY:达到MAX_ENTRIES时剔除的条目部分。 实际比率为1 / CULL_FREQUENCY,因此将CULL_FREQUENCY设置为2可在达到MAX_ENTRIES时剔除一半条目。 此参数应为整数,默认为3。
CULL_FREQUENCY的值为0意味着在达到MAX_ENTRIES时将转储整个缓存。 在一些后端(特别是数据库)上,这会以更多的缓存未命中为代价来更快地进行剔除。
Memcached后端将OPTIONS的内容作为关键字参数传递给客户端构造函数,从而允许对客户端行为进行更高级的控制。 例如,请参见下文。
3.KEY_PREFIX:将自动包含(默认情况下预先添加)到Django服务器使用的所有缓存键的字符串。
1.有关更多信息,请参阅缓存文档:https://docs.djangoproject.com/en/2.1/topics/cache/#cache-key-prefixing
4.VERSION:Django服务器生成的缓存键的默认版本号。
1.有关更多信息,请参阅缓存文档:https://docs.djangoproject.com/en/2.1/topics/cache/#cache-versioning
5. KEY_FUNCTION:包含函数的虚线路径的字符串,该函数定义如何将前缀,版本和密钥组合为最终缓存键。
1.有关更多信息,请参阅缓存文档。https://docs.djangoproject.com/en/2.1/topics/cache/#cache-key-transformation
在此示例中,正在配置文件系统后端,超时为60秒,最大容量为1000个项目:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
'TIMEOUT': 60,
'OPTIONS': {
'MAX_ENTRIES': 1000
}
}
}
这是基于python-memcached的后端的示例配置,对象大小限制为2MB:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
'OPTIONS': {
'server_max_value_length': 1024 * 1024 * 2,
}
}
}
以下是基于pylibmc的后端的示例配置,该后端支持二进制协议,SASL身份验证和ketama行为模式:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
'LOCATION': '127.0.0.1:11211',
'OPTIONS': {
'binary': True,
'username': 'user',
'password': 'pass',
'behaviors': {
'ketama': True,
}
}
}
}
一个网站缓存
设置缓存后,使用缓存的最简单方法是缓存整个站点。 您需要将“django.middleware.cache.UpdateCacheMiddleware”和“django.middleware.cache.FetchFromCacheMiddleware”添加到MIDDLEWARE设置中,如下例所示:
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
]
然后,将以下必需设置添加到Django设置文件中:
1.CACHE_MIDDLEWARE_ALIAS - 用于存储的缓存别名。
2.CACHE_MIDDLEWARE_SECONDS - 每个页面应缓存的秒数。
3.CACHE_MIDDLEWARE_KEY_PREFIX - 如果使用相同的Django安装在多个站点之间共享缓存,请将其设置为站点名称或此Django实例唯一的其他字符串,以防止发生密钥冲突。 如果你不在乎,请使用空字符串。
FetchFromCacheMiddleware缓存状态为200的GET和HEAD响应,请求和响应头允许。 对具有不同查询参数的相同URL的请求的响应被认为是唯一页面并且被单独缓存。 该中间件期望使用与对应的GET请求相同的响应头来回答HEAD请求; 在这种情况下,它可以为HEAD请求返回缓存的GET响应。
此外,UpdateCacheMiddleware会在每个HttpResponse中自动设置几个标头:
1.将Expires标头设置为当前日期/时间加上定义的CACHE_MIDDLEWARE_SECONDS。
- 设置Cache-Control标头以给出页面的最大年龄 - 同样,从CACHE_MIDDLEWARE_SECONDS设置。
- 有关中间件的更多信息,请参见中间件。https://docs.djangoproject.com/en/2.1/topics/http/middleware/
如果视图设置其自己的缓存到期时间(即,它的Cache-Control标头中有max-age部分),则页面将被缓存直到到期时间,而不是CACHE_MIDDLEWARE_SECONDS。 使用django.views.decorators.cache中的装饰器,您可以轻松设置视图的到期时间(使用cache_control()装饰器)或禁用视图的缓存(使用never_cache()装饰器)。 有关这些装饰器的更多信息,请参阅using other headers部分。
如果USE_I18N设置为True,则生成的缓存键将包含活动语言的名称 - 另请参阅Django如何发现语言首选项。 这使您可以轻松缓存多语言网站,而无需自己创建缓存密钥。
当USE_L10N设置为True时,缓存键还包括活动语言,当USE_TZ设置为True时,缓存键还包括当前时区。
视图缓存
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秒。)
每个视图缓存(如每个站点缓存)都是URL的关键字。 如果多个URL指向同一视图,则每个URL将单独缓存。 继续my_view示例,如果您的URLconf看起来像这样:
urlpatterns = [
path('foo/<int:code>/', my_view),
]
那么对/ foo / 1 /和/ foo / 23 /的请求将被单独缓存,正如您所期望的那样。 但是一旦请求了特定的URL(例如,/ foo / 23 /),对该URL的后续请求将使用该缓存。
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。
在URLconf中指定每个视图缓存
上一节中的示例硬编码了视图被缓存的事实,因为cache_page改变了my_view函数。 这种方法将您的视图耦合到缓存系统,由于多种原因,这种方法并不理想。 例如,您可能希望在另一个无缓存站点上重用视图函数,或者您可能希望将视图分发给可能希望在不缓存的情况下使用它们的人员。 解决这些问题的方法是在URLconf中指定每视图缓存,而不是在视图函数本身旁边。
这样做很简单:只需在URLconf中引用视图函数和cache_page即可。 这是以前的旧URLconf:
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)),
]
模板片段缓存
如果您需要更多控制,还可以使用缓存模板标记缓存模板片段。 要让您的模板能够访问此标记,请将{%load cache%}放在模板顶部附近。
{%cache%}模板标记缓存块的内容一段给定的时间。 它至少需要两个参数:缓存超时(以秒为单位)和提供缓存片段的名称。 如果超时为None,则片段将永久缓存。 名称将按原样使用,不要使用变量。 例如:
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
有时,您可能希望根据片段内显示的某些动态数据缓存片段的多个副本。 例如,对于站点的每个用户,您可能需要上一个示例中使用的侧栏的单独缓存副本。 通过将一个或多个附加参数(可能是带或不带过滤器的变量)传递给{%cache%}模板标记来唯一标识缓存片段来执行此操作:
{% load cache %}
{% cache 500 sidebar request.user.username %}
.. sidebar for logged in user ..
{% endcache %}
如果USE_I18N设置为True,则每站点中间件缓存将遵循活动语言。 对于缓存模板标记,您可以使用模板中可用的特定于转换的变量之一来实现相同的结果:
{% load i18n %}
{% load cache %}
{% get_current_language as LANGUAGE_CODE %}
{% cache 600 welcome LANGUAGE_CODE %}
{% trans "Welcome to example.com" %}
{% endcache %}
缓存超时可以是模板变量,只要模板变量解析为整数值即可。 例如,如果模板变量my_timeout设置为值600,则以下两个示例是等效的:
{% cache 600 sidebar %} ... {% endcache %}
{% cache my_timeout sidebar %} ... {% endcache %}
此功能有助于避免模板中的重复。 您可以在一个位置设置变量的超时,然后只重用该值。
默认情况下,缓存标记将尝试使用名为“template_fragments”的缓存。 如果不存在此类缓存,则它将回退到使用默认缓存。 您可以选择备用缓存后端以与using关键字参数一起使用,该参数必须是标记的最后一个参数。
{% cache 300 local-thing ... using="localcache" %}
指定未配置的缓存名称被视为错误。
django.core.cache.utils.``make_template_fragment_key(fragment_name, vary_on=None)
如果要获取用于缓存片段的缓存密钥,可以使用make_template_fragment_key。 fragment_name与缓存模板标记的第二个参数相同; vary_on是传递给标记的所有其他参数的列表。 此函数可用于使缓存项无效或覆盖,例如:
>>> from django.core.cache import cache
>>> from django.core.cache.utils import make_template_fragment_key
# cache key for {% cache 500 sidebar username %}
# {%cache 500 sidebar username%}的缓存键
>>> key = make_template_fragment_key('sidebar', [username])
>>> cache.delete(key) # invalidates cached template fragment
# 使缓存的模板片段无效
低级缓存API
有时候,缓存整个渲染页面并不会让你获得太多收益,实际上是不方便的过度杀伤。
例如,您的站点可能包含一个视图,其结果取决于几个昂贵的查询,其结果会在不同的时间间隔内发生变化。 在这种情况下,使用每站点或每个视图缓存策略提供的整页缓存并不理想,因为您不希望缓存整个结果(因为某些数据经常更改), 但你仍然想要缓存很少改变的结果。
对于这样的情况,Django公开了一个简单的低级缓存API。 您可以使用此API以任意级别的粒度在缓存中存储对象。 您可以缓存任何可以安全pickle的Python对象:字符串,字典,模型对象列表等。 (最常见的Python对象可以被pickle;有关酸洗的更多信息,请参阅Python文档。)
访问缓存
django.core.cache.``caches
您可以通过类似dict的对象访问CACHES设置中配置的缓存:django.core.cache.caches。 在同一个线程中重复请求相同的别名将返回相同的对象。
>>> from django.core.cache import caches
>>> cache1 = caches['myalias']
>>> cache2 = caches['myalias']
>>> cache1 is cache2
True
如果指定的键不存在,将引发InvalidCacheBackendError。
为了提供线程安全性,将为每个线程返回缓存后端的不同实例。
django.core.cache.cache¶
作为快捷方式,默认缓存可用作django.core.cache.cache:
>>> from django.core.cache import cache
该对象相当于缓存[‘default’]。
基本用法
基本界面为:
cache.``set(key, value, timeout=DEFAULT_TIMEOUT, version=None)
>>> cache.set('my_key', 'hello, world!', 30)
cache.``get(key, default=None, version=None)
>>> cache.get('my_key')
'hello, world!'
key应该是str,value可以是任何可选择的Python对象。
timeout参数是可选的,默认为CACHES设置中相应后端的超时参数(如上所述)。 它是值应存储在缓存中的秒数。 为超时传递None将永远缓存该值。 超时0不会缓存该值。
如果缓存中不存在该对象,则cache.get()返回None:
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key')
None
我们建议不要在缓存中存储文字值None,因为您将无法区分存储的None值和由返回值None表示的缓存未命中。
cache.get()可以采用默认参数。 如果缓存中不存在该对象,则指定要返回的值:
>>> cache.get('my_key', 'has expired')
'has expired'
cache.``add(key, value, timeout=DEFAULT_TIMEOUT, version=None)
若要仅在键不存在时添加它,请使用add()方法。它采用与set()相同的参数,但是如果指定的密钥已经存在,它将不会尝试更新缓存:
>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'
如果您需要知道add()是否在缓存中存储了一个值,您可以检查返回值。 如果存储了值,它将返回True,否则返回False。
cache.``get_or_set(key, default, timeout=DEFAULT_TIMEOUT, version=None)
如果要获取键值或者如果键不在缓存中,则设置值,则有get_or_set()方法。 它采用与get()相同的参数,但默认设置为该键的新缓存值,而不是简单地返回:
>>> cache.get('my_new_key') # returns None
>>> cache.get_or_set('my_new_key', 'my new value', 100)
'my new value'
您还可以将任何可调用的值作为默认值传递:
>>> import datetime
>>> cache.get_or_set('some-timestamp-key', datetime.datetime.now)
datetime.datetime(2014, 12, 11, 0, 15, 49, 457920)
cache.``get_many(keys, version=None)
还有一个get_many()接口只能访问缓存一次。 get_many()返回一个字典,其中包含您要求的所有密钥,这些密钥实际存在于缓存中(并且尚未过期):
>>> cache.set('a', 1)
>>> cache.set('b', 2)
>>> cache.set('c', 3)
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
cache.``set_many(dict, timeout)
要更有效地设置多个值,请使用set_many()传递键值对的字典:
>>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
与cache.set()类似,set_many()采用可选的超时参数。
在支持的后端(memcached),set_many()返回未能插入的密钥列表。
cache.``delete(key, version=None)
您可以使用delete()显式删除键。 这是清除特定对象的缓存的简单方法:
>>> cache.delete('a')
cache.``delete_many(keys, version=None)
如果您想一次清除一组密钥,delete_many()可以列出要清除的密钥:
>>> cache.delete_many(['a', 'b', 'c'])
cache.``clear()
最后,如果要删除缓存中的所有密钥,请使用cache.clear()。 小心这个; clear()将从缓存中删除所有内容,而不仅仅是应用程序设置的密钥。
>>> cache.clear()
cache.``touch(key, timeout=DEFAULT_TIMEOUT, version=None)
cache.touch()为键设置新的到期时间。 例如,要将密钥更新为从现在起10秒后过期:
>>> cache.touch('a', 10)
True
与其他方法一样,timeout参数是可选的,默认为CACHES设置中相应后端的TIMEOUT选项。
如果成功触摸按键,touch()返回True,否则返回False。
-
cache.``incr(key, delta=1, version=None)
-
cache.``decr(key, delta=1, version=None)
您还可以分别使用incr()或decr()方法递增或递减已存在的键。 默认情况下,现有缓存值将递增或递减1.可以通过为递增/递减调用提供参数来指定其他递增/递减值。 如果您尝试增加或减少不存在的缓存键,将引发ValueError:
>>> cache.set('num', 1)
>>> cache.incr('num')
2
>>> cache.incr('num', 10)
12
>>> cache.decr('num')
11
>>> cache.decr('num', 5)
6
cache.``close()
如果由缓存后端实现,则可以使用close()关闭与缓存的连接。
>>> cache.close()
缓存密钥前缀
如果要在服务器之间或生产环境与开发环境之间共享缓存实例,则一台服务器缓存的数据可能会被另一台服务器使用。 如果服务器之间的缓存数据格式不同,则可能导致一些非常难以诊断的问题。
为了防止这种情况,Django提供了为服务器使用的所有缓存密钥添加前缀的功能。 当保存或检索特定的缓存键时,Django将自动为缓存键添加KEY_PREFIX缓存设置的值。
通过确保每个Django实例具有不同的KEY_PREFIX,您可以确保缓存值中不会发生冲突。
缓存版本控制
更改使用缓存值的运行代码时,可能需要清除任何现有缓存值。 最简单的方法是刷新整个缓存,但这可能会导致丢失仍然有效且有用的缓存值。
Django提供了一种更好的方法来定位单个缓存值。 Django的缓存框架具有系统范围的版本标识符,使用VERSION缓存设置指定。 此设置的值将自动与缓存前缀和用户提供的缓存键组合以获取最终缓存键。
默认情况下,任何密钥请求都将自动包含站点默认缓存密钥版本。 但是,原始缓存函数都包含版本参数,因此您可以指定要设置或获取的特定缓存密钥版本。 例如:
>>> # Set version 2 of a cache key
>>> cache.set('my_key', 'hello world!', version=2)
>>> # Get the default version (assuming version=1)
>>> cache.get('my_key')
None
>>> # Get version 2 of the same key
>>> cache.get('my_key', version=2)
'hello world!'
可以使用incr_version()和decr_version()方法递增和递减特定键的版本。 这使特定键可以碰撞到新版本,而其他键不受影响。 继续前面的例子:
>>> # Increment the version of 'my_key'
>>> cache.incr_version('my_key')
>>> # The default version still isn't available
>>> cache.get('my_key')
None
# Version 2 isn't available, either
>>> cache.get('my_key', version=2)
None
>>> # But version 3 *is* available
>>> cache.get('my_key', version=3)
'hello world!'
缓存密钥转换
如前两节所述,用户提供的缓存密钥不是逐字使用的 - 它与缓存前缀和密钥版本相结合,以提供最终缓存密钥。 默认情况下,使用冒号连接这三个部分以生成最终字符串:
def make_key(key, key_prefix, version):
return ':'.join([key_prefix, str(version), key])
如果要以不同方式组合部件,或者对最终密钥应用其他处理(例如,获取关键部件的哈希摘要),则可以提供自定义键功能。
KEY_FUNCTION缓存设置指定与上面的make_key()原型匹配的函数的虚线路径。 如果提供,将使用此自定义键功能而不是默认键组合功能。
缓存密钥警告
Memcached是最常用的生产缓存后端,它不允许超过250个字符的缓存键或包含空格或控制字符,并且使用此类键会导致异常。 为了鼓励缓存可移植代码并最大限度地减少令人不快的意外,如果使用会导致memcached错误的密钥,则其他内置缓存后端会发出警告(django.core.cache.backends.base.CacheKeyWarning)。
如果您使用的生产后端可以接受更广泛的密钥(自定义后端或其中一个非memcached内置后端),并希望在没有警告的情况下使用更宽范围,则可以使用此代码使CacheKeyWarning静音 在您的一个INSTALLED_APPS的管理模块中:
import warnings
from django.core.cache import CacheKeyWarning
warnings.simplefilter("ignore", CacheKeyWarning)
如果您想为其中一个内置后端提供自定义密钥验证逻辑,则可以对其进行子类化,仅覆盖validate_key方法,并按照使用自定义缓存后端的说明进行操作。 例如,要为locmem后端执行此操作,请将此代码放在模块中:
from django.core.cache.backends.locmem import LocMemCache
class CustomLocMemCache(LocMemCache):
def validate_key(self, key):
"""Custom validation, raising exceptions or warnings as needed."""
...
…并在CACHES设置的BACKEND部分中使用虚线Python路径到此类
下游缓存
到目前为止,本文档主要关注缓存您自己的数据。 但另一种类型的缓存也与Web开发相关:由“下游”缓存执行的缓存。 这些系统甚至在请求到达您的网站之前就为用户缓存页面。
以下是下游缓存的一些示例:
1.您的ISP可能会缓存某些页面,因此如果您从https://example.com/请求了一个页面,您的ISP会向您发送该页面而无需直接访问example.com。 example.com的维护者不知道这个缓存; ISP位于example.com和您的Web浏览器之间,透明地处理所有缓存。
2.您的Django网站可能位于代理缓存之后,例如Squid Web代理缓存(http://www.squid-cache.org/),它会缓存页面以提高性能。 在这种情况下,每个请求首先由代理处理,只有在需要时才会传递给您的应用程序。
3.您的Web浏览器也会缓存页面。 如果网页发送了相应的标题,则您的浏览器将使用本地缓存副本来发送对该页面的后续请求,甚至无需再次联系网页以查看其是否已更改。
下游缓存是一个很好的效率提升,但它有一个危险:许多网页的内容根据身份验证和许多其他变量而有所不同,并且基于URL盲目保存页面的缓存系统可能会将不正确或敏感数据暴露给后续 这些页面的访问者。
例如,假设您运行Web电子邮件系统,“收件箱”页面的内容显然取决于登录的用户。如果ISP盲目缓存您的站点,那么通过该ISP登录的第一个用户将拥有他们的 为该网站的后续访问者缓存的特定于用户的收件箱页面。 这不酷。
幸运的是,HTTP提供了解决此问题的方法。 存在许多HTTP头以指示下游缓存根据指定的变量区分其缓存内容,并告知缓存机制不缓存特定页面。 我们将在后面的部分中介绍其中的一些标题。
使用Vary标头
Vary头定义了缓存机制在构建其缓存密钥时应考虑的请求头。 例如,如果网页的内容取决于用户的语言偏好,则该页面被称为“因语言而异”。
默认情况下,Django的缓存系统使用请求的完全限定URL创建其缓存密钥 - 例如,“https://www.example.com/stories/2005/?order_by=author”。 这意味着对该URL的每个请求都将使用相同的缓存版本,无论用户代理差异如cookie或语言首选项如何。 但是,如果此页面根据请求标头中的某些差异(例如cookie,语言或用户代理)生成不同的内容,则需要使用Vary标头来告知页面输出所依赖的缓存机制 那些事。
要在Django中执行此操作,请使用方便的django.views.decorators.vary.vary_on_headers()视图装饰器,如下所示:
from django.views.decorators.vary import vary_on_headers
@vary_on_headers('User-Agent')
def my_view(request):
...
在这种情况下,缓存机制(例如Django自己的缓存中间件)将为每个唯一的用户代理缓存单独的页面版本。
使用vary_on_headers装饰器而不是手动设置Vary头(使用诸如response [‘Vary’] ='user-agent’之类的东西)的优点是装饰器添加到Vary头(可能已经存在),而不是设置 它从头开始,可能会覆盖已存在的任何东西。
您可以将多个标头传递给vary_on_headers():
@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
...
这告诉下游缓存两者都有所不同,这意味着user-agent和cookie的每个组合都将获得自己的缓存值。 例如,具有用户代理Mozilla和cookie值foo = bar的请求将被视为与具有用户代理Mozilla和cookie值foo = ham的请求不同。
因为cookie的变化很常见,所以有一个django.views.decorators.vary.vary_on_cookie()装饰器。 这两个视图是等效的:
@vary_on_cookie
def my_view(request):
...
@vary_on_headers('Cookie')
def my_view(request):
...
传递给vary_on_headers的标头不区分大小写; “User-Agent”与“user-agent”相同。
您也可以直接使用辅助函数django.utils.cache.patch_vary_headers()。 此函数设置或添加Vary标头。 例如:
from django.shortcuts import render
from django.utils.cache import patch_vary_headers
def my_view(request):
...
response = render(request, 'template_name', context)
patch_vary_headers(response, ['Cookie'])
return response
patch_vary_headers将HttpResponse实例作为其第一个参数,将不区分大小写的头名称的列表/元组作为其第二个参数。
有关Vary标题的更多信息,请参阅官方的Vary规范。
控制缓存:使用其他标头
缓存的其他问题是数据的隐私性以及数据应该存储在级联缓存中的问题。
用户通常面对两种缓存:他们自己的浏览器缓存(私有缓存)和他们的提供者缓存(公共缓存)。 公共缓存由多个用户使用,并由其他人控制。 这会导致敏感数据出现问题 - 例如,您不希望存储在公共缓存中的银行帐号。 因此,Web应用程序需要一种方法来告诉缓存哪些数据是私有的,哪些是公共的。
解决方案是指示页面的缓存应该是“私有的”。要在Django中执行此操作,请使用cache_control()视图装饰器。 例:
from django.views.decorators.cache import cache_control
@cache_control(private=True)
def my_view(request):
...
这个装饰器负责在幕后发送适当的HTTP头。
请注意,缓存控制设置“private”和“public”是互斥的。 如果应该设置“private”,装饰器确保删除“public”指令(反之亦然)。 两个指令的示例使用将是提供私人和公共条目的博客站点。 公共条目可以缓存在任何共享缓存上。 以下代码使用patch_cache_control(),手动方式修改缓存控件头(它由cache_control()装饰器在内部调用):
from django.views.decorators.cache import patch_cache_control
from django.views.decorators.vary import vary_on_cookie
@vary_on_cookie
def list_blog_entries_view(request):
if request.user.is_anonymous:
response = render_only_public_entries()
patch_cache_control(response, public=True)
else:
response = render_private_and_public_entries(request.user)
patch_cache_control(response, private=True)
return response
您也可以通过其他方式控制下游缓存(有关HTTP缓存的详细信息,请参阅RFC 7234)。 例如,即使您不使用Django的服务器端缓存框架,您仍然可以告诉客户端使用max-age指令缓存视图一段时间:
from django.views.decorators.cache import cache_control
@cache_control(max_age=3600)
def my_view(request):
...
(如果您确实使用了缓存中间件,它已经使用CACHE_MIDDLEWARE_SECONDS设置的值设置了max-age。在这种情况下,cache_control()装饰器中的自定义max_age将优先,并且标头值将正确合并。 )
任何有效的Cache-Control响应指令在cache_control()中都有效。 以下是一些例子:
- no_transform=True
- must_revalidate=True
- stale_while_revalidate=num_seconds
可以在IANA注册表中找到已知指令的完整列表(请注意,并非所有指令都适用于响应)。
如果您想使用标头来完全禁用缓存,则never_cache()是一个视图装饰器,它添加标头以确保浏览器或其他缓存不会缓存响应。 例:
from django.views.decorators.cache import never_cache
@never_cache
def myview(request):
...
中间件的顺序
如果使用缓存中间件,将每一半放在MIDDLEWARE设置中的正确位置非常重要。 这是因为缓存中间件需要知道哪些标头可以改变缓存存储。 中间件总是会在Vary响应头中添加一些东西。
UpdateCacheMiddleware在响应阶段运行,其中中间件以相反的顺序运行,因此列表顶部的项目在响应阶段最后运行。 因此,您需要确保UpdateCacheMiddleware出现在可能向Vary标头添加内容的任何其他中间件之前。 以下中间件模块执行此操作:
- SessionMiddleware adds Cookie
- GZipMiddleware adds Accept-Encoding
- LocaleMiddleware adds Accept-Language
另一方面,FetchFromCacheMiddleware在请求阶段运行,其中中间件首先应用于中间件,因此列表顶部的项目在请求阶段首先运行。 FetchFromCacheMiddleware还需要在其他中间件更新Vary头之后运行,因此FetchFromCacheMiddleware必须在任何项目之后执行。