Django缓存框架详解(官方文档翻译来)

首先看Django官网的介绍:

    动态网站的一个基本权衡就是他们是动态的,每次一个用户请求一个页面,web服务器进行各种各样的计算-从数据库查询到模板渲染到业务逻辑-从而生成站点访问者看到的页面。从处理开销的角度来看,相比标准的从文件系统读取文件的服务器调度,这是昂贵了不少。尽管对于大多数网站来说,这种开销不是什么大问题,因为大多数web应用不过是想学学院的首页那样,都是小到中型的站点,流量也很少。但对于中到大型的站点来说,必须尽可能的减少开销。这就是缓存的由来。
    django自带一个强大的缓存系统,提供不同层次的缓存粒度:你可以缓存某个视图函数的输出,或者只是某个特别难生成的部分或者是整个站点。同时django也有类似“上流”缓存的东西,类似于Squid和基于浏览器的缓存,这类缓存是你无法直接控制但是你可以通过一些提示(比如http头部)指定你网站的那些部分需要和如何缓存。

缓存系统工作原理:

    对于一个给定的url,尝试从缓存中找到对应的url网址,检查页面是否存在缓存中,如果存在,直接将缓存中的页面返回,如果缓存中没有,一系列操作(比如数据库查询)后,将内容页面保存到缓存系统中以供下次使用,并将生成的页面返回给前端。

设置缓存:

    缓存系统需要少量的配置才能使用,你必须告诉系统你的缓存数据存放在哪里-数据库还是文件系统亦或是直接存在缓存中-这是一个重要的决定,直接影响到你的缓存性能;当然,一些缓存类型本来就是比其他的快,这我们无法回避。

        在项目的setting.py里面可以通过CACHES配置缓存,django中可用的缓存系统有Memcached、数据库、文件、本地内存,下面一一讲解。

Memcached:

    Memcached是Django本地支持的最快速,最高效的高速缓存类型,它是一个完全基于内存的缓存服务器,最初开发用于处理LiveJournal.com的高负载,随后由Danga Interactive开源。 Facebook和维基百科等网站使用它来减少数据库访问并显着提高网站性能。它用于动态Web应用以减轻数据库负载从而显著提供网站性能,也是django中到目前为止最有效率的可用缓存。

    Memcached作为守护进程运行,并分配了指定数量的RAM。 它所做的就是提供一个快速接口来添加,检索和删除缓存中的数据。 所有数据都直接存储在内存中,因此不会产生数据库或文件系统使用的开销。

    在安装Memcached之后,您需要安装Memcached绑定。 有几个Python Memcached绑定可用; 最常见的两个是python-memcached和pylibmc。

    在Django中使用Memcached:
 将BACKEND设置为django.core.cache.backends.memcached.MemcachedCache或django.core.cache.backends.memcached.PyLibMCCache(具体取决于您选择的memcached绑定)
          将LOCATION设置为ip:port values,其中ip是Memcached守护程序的IP地址,port是运行Memcached的端口,或者是unix:路径值,其中path是到达Memcached Unix套接字文件的路径。

    在本例中,Memcached使用python-memcached绑定在本地主机(127.0.0.1)端口11211上运行:

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

在本例中,Memcached通过使用python-memcached绑定的本地Unix套接字文件/tmp/memcached.sock可用:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        '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.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可以将其缓存的数据存储在数据库中。 如果你有一个快速,索引良好的数据库服务器,这最好。
将数据库表用作缓存后端:
将BACKEND设置为django.core.cache.backends.db.DatabaseCache
将LOCATION设置为表名,即数据库表的名称。 这个名称可以是任何你想要的,只要它是一个尚未在数据库中使用的有效表名即可。在这个例子中,缓存表的名字是my_cache_table:  
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
    }
}

    在上面操作前,我们还没建好缓存表,所以首先我们应该创建好缓存表,使用如下命令,注意cache_table_name不要和数据库中已经存在的表名冲突:

python manage.py createcachetable [cache_table_name]  #这里cache_table_name即为上面的my_cache_table
    这将在您的数据库中创建一个表格,该表格采用Django的数据库缓存系统所需的适当格式。 该表的名称取自LOCATION。
    如果您使用多个数据库缓存,createcachetable会为每个缓存创建一个表。
    如果您使用多个数据库,createcachetable会观察数据库路由器的allow_migrate()方法。
    像迁移一样,createcachetable不会触及现有的表。 它只会创建缺少的表格。

    要打印将要运行的SQL,而不是运行它,请使用createcachetable --dry-run选项。

文件系统缓存:

    基于文件的后端将每个缓存值序列化并存储为单独的文件。 要使用此后端将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',
        #'LOCATION': 'c:/foo/bar',#windows下的示例
    }
}

注意:目录路径应该是绝对的 - 也就是说,它应该从文件系统的根目录开始。 在设置结束时是否放置斜线并不重要。必须保证服务器对你列出的路径具有读写权限。

本地内存缓存:

    如果你想具有内存缓存的优点但有没有能力运行Memcached的时候,你可以考虑本地内存缓存,这个缓存是多进程和线程安全的,后端设置为django.core.cache.backends.lovMemCache

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

    缓存LOCATION用来区分每个内存存储,如果你只有一个本地内存缓存,你可以忽略这个设置;但如果你有多个的时候,你需要至少给他们中一个赋予名字以区分他们。缓存使用最近最少使用(LRU)剔除策略。注意每个进程都有它们自己的私有缓存实例,所以跨进陈缓存是不可能的,因此,本地内存缓存不是特别有效率的,建议你只是在内部开发测时使用,不建议在生产环境中使用。

虚拟缓存:

这在实际应用中时非常有用的,假如你有一个产品用发布的时候非常需要用到缓存,但在开发和测试的时候你并不想使用缓存,同时你也不想等到产品发布的时候特别的去修改缓存相关的代码,那么你可以是用虚拟缓存,要启用虚拟缓存,你只需按下面的例子去配置你的后端。

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
    }
}

自定义的缓存:

虽然Django支持大量缓存后端,但有时您可能需要使用自定义的缓存后端。 要在Django中使用外部缓存后端,请使用Python导入路径作为CACHES设置的BACKEND,如下所示:

CACHES = {
    'default': {
        'BACKEND': 'path.to.backend',
    }
}

缓存参数:

每个缓存后端可以被赋予额外的参数来控制缓存行为。这些参数作为CACHES设置中的其他键提供。有效参数如下:
TIMEOUT:用于缓存的默认超时时间(以秒为单位)。该参数默认为300秒(5分钟)。您可以将TIMEOUT设置为无,以便默认情况下缓存密钥永不过期。值为0将导致密钥立即过期(实际上“不缓存”)。

OPTIONS:应该传递给缓存后端的任何选项。有效选项的列表将随每个后端而变化,而由第三方库支持的缓存后端将直接将其选项传递到基础缓存库。
    实现自己的剔除策略(即,locmem,文件系统和数据库后端)的高速缓存后端将遵循以下选项:
                MAX_ENTRIES:删除旧值之前缓存中允许的最大条目数。这个参数默认为300。
            CULL_FREQUENCY:达到MAX_ENTRIES时被剔除的条目的比例。实际比例为1 / CULL_FREQUENCY,因此在达到            MAX_ENTRIES时将CULL_FREQUENCY设置为2以剔除一半条目。这个参数应该是一个整数,默认为3。
对于CULL_FREQUENCY,值为0表示在达到MAX_ENTRIES时将转储整个缓存。在某些后端(特别是数据库),这会以更多的缓存未命中为代价来快速剔除。
Memcached后端将OPTIONS的内容作为关键字参数传递给客户端构造函数,允许更高级地控制客户端行为。例如用法,见下文。

KEY_PREFIX:一个字符串,将自动包含在Django服务器使用的所有缓存键中(默认预置)。

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,
        }
    }
}

设置每个站点的缓存:

        除了选择和设置好前面的缓存系统,还要做些其他额外的配置才行。

  缓存设置完成后,使用缓存的最简单方法是缓存整个网站。 您需要将'django.middleware.cache.UpdateCacheMiddleware'和'django.middleware.cache.FetchFromCacheMiddleware'添加到setting.py文件中的MIDDLEWARE项设置中,如下例所示(设置中间件):

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

    注意:UpdateCache中间件一定要放在第一位,Fetch中间件必须放最后(因为中间件的顺序决定着运行的顺序)

 然后,将以下必需设置添加到您的Django设置文件中:
    CACHE_MIDDLEWARE_ALIAS ------------------用于存储的高速缓存别名。
    CACHE_MIDDLEWARE_SECONDS -------------每个页面应该被缓存的秒数,默认600。

    CACHE_MIDDLEWARE_KEY_PREFIX ----------如果使用相同的Django安装跨多个站点共享缓存,请将其设置为站 点 名称或此Django实例唯一的其他字符串,以防止发生重大冲突。 如果你不在乎,请使用空字符串。如下例子:

CACHE_MIDDLEWARE_ALIAS = 'default'    #用来存储的缓存别名
CACHE_MIDDLEWARE_SECONDS = 0  #所有页面默认缓存时间,默认600
CACHE_MIDDLEWARE_KEY_PREFIX ='www.demo.com'  #关键的前缀,当多个站点使用同一个配置的时候,这个可以设置可以避免发生冲突,一般设置为网站域名
CACHE_MIDDLEWARE_ANONYMOUS_ONLY = False #那么只有匿名的请求会被缓存,这是一个禁用缓存非匿名用户页面的最简单的做法,注意确保已经启用了Django用户认证中间件

每个视图的缓存:

    使用缓存框架的更细化的方式是缓存单个视图的输出。 django.views.decorators.cache定义了一个cache_page修饰器,它可以自动缓存视图的响应。 它很容易使用(请注意,为了便于阅读,我们将其写为60 * 15, 即15分钟。还要记得要先导入cache_page):

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request):
    ...
    cache_page(timeout, [cache=cache name], [key_prefix=key prefix])
    cache_page只接受一个参数和两个关键字参数,
  1.         timeout是缓存时间,以秒为单位
  2.         cache:指定使用你的CACHES设置中的哪一个缓存后端
  3.         key_prefix:指定缓存前缀,可以覆盖在配置文件中CACHE_MIDDLEWARE_KEY_PREFIX的值
    按照每个视图的缓存,就像每个站点的缓存一样,从URL中取消。 如果多个网址指向相同的视图,则每个网址将分开缓存。 继续my_view示例,如果您的URLconf如下所示:
urlpatterns = [
    path('foo//', 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):
    ...

在url配置文件中指定每个视图的缓存:

    上一节中的示例对视图进行了缓存这一事实进行了硬编码,因为cache_page会更改my_view函数。 这种方法将您的观点与高速缓存系统耦合在一起,由于多种原因,这并不理想。 例如,您可能希望在另一个无缓存站点上重用视图函数,或者您可能希望将视图分发给可能希望在不缓存的情况下使用它们的人员。 解决这些问题的方法是在URLconf中指定每个视图缓存,而不是在视图函数本身旁边。
    这样做很简单:只需在URLconf中引用视图函数时,可以使用cache_page包装视图函数:
from django.views.decorators.cache import cache_page

urlpatterns = [
    path('foo//', cache_page(60 * 15)(my_view)),
]

模板片段缓存:

    如果您在更多控制之后,还可以使用缓存模板标记来缓存模板片段。 要让您的模板可以访问此代码,请将{%load cache%}放在模板顶部附近。
{%cache%}模板标签将块的内容缓存给定的时间量。 它至少需要两个参数:以秒为单位的高速缓存超时以及给出高速缓存片段的名称。 如果超时时间为无,片段将永久缓存。 该名称将被视为不使用变量。 例如:
{% load cache %}
{% cache 500 sidebar %}
    .. sidebar ..
{% endcache %}
    有时您可能想要缓存片段的多个副本,具体取决于片段中出现的一些动态数据。 例如,您可能需要为您的网站的每个用户使用上一个示例中使用的侧栏的单独缓存副本。 通过向{%cache%}模板标签传递一个或多个可能带有或不带过滤器的变量的附加参数来唯一标识缓存片段:
{% load cache %}
{% cache 500 sidebar request.user.username %}
    .. sidebar for logged in user ..
{% endcache %}

底层缓存API:

    有时候我们并不想缓存整个视图,只是想缓存某个数据库检索的结果,事实上有时候缓存整个渲染页面并不会给你带来太多好处。例如,也许您的网站包含一个视图,其结果取决于几个复杂的查询,其结果会以不同的时间间隔进行更改。 在这种情况下,使用每个站点或每个视图缓存策略提供的整页缓存并不理想,因为您不希望缓存整个结果(因为一些数据经常变化), 但你仍然想缓存很少改变的结果。
    对于这种情况,Django公开了一个简单的底层缓存API。 您可以使用此API将对象以任意级别的粒度存储在缓存中。 您可以缓存任何可以安全序列化的Python对象:字符串,字典,模型对象列表等。 
    访问缓存
        您可以通过类似dict的对象访问在CACHES设置中配置的缓存:django.core.cache.caches。 在同一线程中重复请求相同的别名将返回相同的对象。
>>> from django.core.cache import caches
>>> cache1 = caches['myalias']
>>> cache2 = caches['myalias']
>>> cache1 is cache2
True
    基本用法:
>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
'hello, world!'
键应该是一个str,并且值可以是任何可Python对象。
超时参数是可选的,默认为CACHES设置中相应后端的超时参数(如上所述)。 这是值应该存储在缓存中的秒数。 传入无超时将永远缓存该值。 超时0不会缓存该值。
如果该对象不存在于缓存中,则cache.get()返回None:
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key')
None
只有在键不存在的情况下才能添加键,请使用add()方法。 它采用与set()相同的参数,但如果指定的键已经存在,它不会尝试更新缓存:
>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'
还有一个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}
您可以使用delete()显式删除键。 这是清除特定对象缓存的简单方法:
>>> cache.delete('a')

缓存关键子前缀:

    缓存键前缀
        如果您在服务器之间或生产环境与开发环境之间共享缓存实例,则可能由另一台服务器使用一台服务器缓存的数据。 如果服务器之间缓存数据的格式不同,这可能会导致一些非常难以诊断的问题。
        为了防止出现这种情况,Django提供了为服务器使用的所有缓存密钥加前缀的功能。 当保存或检索特定的缓存键时,Django会自动为缓存键添加KEY_PREFIX缓存设置的值。
        通过确保每个Django实例具有不同的KEY_PREFIX,可以确保缓存值中不会发生冲突。

“上流”缓存:

    到目前为止,这个文档专注于缓存你自己的数据,即服务端的缓存。但另一种类型的缓存也与Web开发密切相关:由“上流”缓存执行的缓存,这些系统甚至在请求到达您的网站之前就可以为用户缓存页面。即让在客户端上实现缓存。

下面是一些上流缓存的例子:
  1. 你的ISP(因特网提供商)可能会缓存一些页面,比如你访问   http://example.com/ 的时候,你的ISP可能没有访问到 example.com就把页面返回给你了 
  2. 你的浏览器可能也会缓存一些网页
    上游缓存是一个很好的效率提高,但不适合缓存所有的网页,特别是一些跟认证有关的页面的缓存可能会给网站带来一些隐患,那该如何决定是否在上游缓存呢?
    幸运的是,HTTP为这个问题提供了一个解决方案。存在许多HTTP标头以指示下游高速缓存根据指定的变量来区分它们的高速缓存内容,并且指示高速缓存机构不高速缓存特定页面。我们将在后面的章节中查看一些这些标题。

控制缓存 使用其他头部信息:

        缓存的其他问题是数据的隐私性以及数据应该存储在缓存级联中的问题。
用户通常面对两种缓存:他们自己的浏览器缓存(私有缓存)和他们的提供者缓存(公共缓存)。 公用缓存由多个用户使用并由其他人控制。 这会导致敏感数据出现问题 - 您不希望将您的银行帐号存储在公共缓存中。 因此,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_control接收到额参数:
  • public=True
  • private=True
  • no_cache=True
  • no_transform=True
  • must_revalidate=True
  • proxy_revalidate=True
  • max_age=num_seconds
  • s_maxage=num_seconds

MIDDLEWARE的顺序:

    UpdateCacheMiddleware在响应阶段运行,中间件以相反的顺序运行,所以列表顶部的项目在响应阶段最后运行。因此,您需要确保UpdateCacheMiddleware出现在可能会将某些内容添加到Vary标头的任何其他中间件之前。以下中间件模块这样做:
  • SessionMiddleware添加Cookie
  • GZipMiddleware添加了Accept-Encoding
  • LocaleMiddleware添加了Accept-Language
    另一方面,FetchFromCacheMiddleware在请求阶段运行,中间件首先应用于中间件,因此列表顶部的项目在请求阶段首先运行。 FetchFromCacheMiddleware还需要在其他中间件更新Vary头后运行,因此FetchFromCacheMiddleware必须位于任何项目之后。



你可能感兴趣的:(Django)