【Django缓存实现】前端缓存和后端缓存

2023-11-11

目录

一、什么是缓存?

二、Web缓存

(一)前端缓存

(二)后端缓存

三、Django缓存

(一)缓存类型

(二)设置缓存

1.Memcached

2.Redis

3.数据库缓存

4.文件系统缓存

5.本地内存缓存

6.虚拟缓存(用于开发模式)

7.使用自定义缓存后端

8.缓存参数

(四)站点缓存

(五)视图缓存

(六)在 URLconf 中指定视图缓存

(七)模板片段缓存

(八)底层缓存 API

(九)访问缓存

四、基本用法

1.缓存键前缀

2.缓存版本控制

3.缓存键转换

4.缓存键警告

五、异步支持

六、注意事项


一、什么是缓存?

缓存的定义:缓存(cache),原始意义是指访问速度比一般随机存取存储器(RAM)快的一种高速存储器,通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。缓存的设置是所有现代计算机系统发挥高性能的重要因素之一。

缓存的优点汇总,加快页面打开速度,减少网络带宽消耗,降低服务器压力。

具体工作原理可参考:缓存_百度百科 (baidu.com)。通俗的说,这里涉及到计算机的各种存储,内存、磁盘、cpu等都算是计算机的存储器。
 

二、Web缓存

我们的django程序是计算机的一个进程,进程是运行在内存上的,当用户访问时,会在进程中开启一个线程处理请求。

(一)前端缓存

前端缓存即浏览器缓存,可以解决减轻请求服务器的频次、提高页面展示效率。在实际开发中,因网站的性质不同,往往会考虑缓存的问题。一般的展示型网站(官网、介绍等)为了减轻请求服务器的频次、提高页面展示效率将设置缓存配置。

前端缓存是根据请求头headers的expires和cache-control判断是否重新请求服务器,缓存的对象为html、css、js、图片等静态文件。

(二)后端缓存

通常情况下,计算一个值是很昂贵的(也就是耗费资源,而且速度很慢),所以把保存到快速访问的缓存中,为下次需要时做好准备,会有巨大的好处。

Django 自带强大的缓存系统,可以让你保存动态页面,这样就不必为每次请求计算。为了方便,Django 提供了不同级别的缓存粒度。你可以缓存特定视图的输出,你可以只缓存难以生成的部分,或者你可以缓存整个网站。

三、Django缓存

(一)缓存类型

Memcached:一种高性能的分布式内存对象缓存系统,用于动态网站,以减轻数据库负载。

Redis:

数据库缓存:缓存信息存储在网站数据库的缓存表中,缓存表可以在项目的配置文件中配置,适合大中型网站使用。

文件系统缓存:缓存信息以文本文件格式保存,适合中小型网站使用。

本地内存缓存:django默认的缓存保存方式,将缓存存放在项目所在系统的内存中,之适用于项目开发测试。

虚拟缓存:django内置的虚拟缓存,市级上只提供缓存接口,并不能存储缓存的相关配置。

(二)设置缓存

缓存系统需要少量的设置。也就是说,你必须告诉它你的缓存数据应该放在哪里 —— 是在数据库中,还是在文件系统上,或者直接放在内存中。这是一个重要的决定,会影响你的缓存的性能;是的,有些缓存类型比其他类型快。

缓存设置项位于你的配置文件的缓存配置中。这里有缓存配置所有可用值的说明。

1.Memcached

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

关于 Memcached 的最后一点是,基于内存的缓存有一个缺点:因为缓存的数据存储在内存中,如果你的服务器崩溃,数据将丢失。显然,内存并不是用来永久存储数据的,所以不要依赖基于内存的缓存作为你唯一的数据存储。毫无疑问,Django 缓存后端中的 每个 都不应该用于永久存储 —— 它们的目的都是为了缓存的解决方案,而不是存储 —— 但我们在这里指出这一点是因为基于内存的缓存是格外临时的。

Changed in Django 3.2:

增加了 PyMemcacheCache 后端。

3.2 版后已移除:MemcachedCache` 后端已被废弃,因为 python-memcached 有一些问题,而且似乎没有维护。使用 PyMemcacheCache 或 PyLibMCCache 代替。

2.Redis

这是一个可用于缓存的内存数据库。首先,您需要在本地或远程计算机上运行Redis服务器。

设置完Redis服务器后,需要为Redis安装Python绑定。redis-py是Django本机支持的绑定。还建议安装额外的hiredispy包。

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379',
    }
}

通常,Redis服务器受身份验证保护。为了提供用户名和密码,请将其与URL一起添加到LOCATION中:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://username:password@127.0.0.1:6379',
    }
}

如果在复制模式下设置了多个Redis服务器,则可以将服务器指定为分号或逗号分隔的字符串或列表。当使用多个服务器时,在第一个服务器(引导服务器)上执行写入操作。读取操作在随机选择的其他服务器(副本)上执行:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': [
            'redis://127.0.0.1:6379', # leader
            'redis://127.0.0.1:6378', # read-replica 1
            'redis://127.0.0.1:6377', # read-replica 2
        ],
    }
}

3.数据库缓存

Django 可以在数据库中存储缓存数据。如果你有一个快速、索引正常的数据库服务器,这种缓存效果最好。

在这个例子中,缓存表的名称是 my_cache_table :

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
    }
}

与其他缓存后端不同,数据库缓存不支持在数据库级别自动筛选过期条目。相反,每次调用add()、set()或touch()时,都会删除过期的缓存条目。

注意:使用数据库缓存之前,必须通过下面的命令创建缓存表:

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

如果你没有指定路由指向数据库缓存模型,缓存后端将使用 默认 的数据库。

如果没使用数据库缓存后端,则无需担心为数据库缓存模型提供路由指令。

4.文件系统缓存

基于文件的后端序列化并保存每个缓存值作为单独的文件。要使用此后端,可将 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',
    }
}

目录路径应该是绝对路径——因此,它应该以文件系统根目录开始。无需担心是否需要以斜杠结尾。

Make sure the directory pointed-to by this setting either exists and is readable and writable, or that it can be created by the system user under which your web server runs. Continuing the above example, if your server runs as the user apache, make sure the directory /var/tmp/django_cache exists and is readable and writable by the user apache, or that it can be created by the user apache.

警告

当缓存 LOCATION 包含在 MEDIA_ROOT 或 STATICFILES_FINDERS 中,敏感数据可能被暴露。

获得访问缓存文件的攻击者不仅可以伪造 HTML 内容,你的网站会信任它,而且还可以远程执行任意代码,因为数据是用 pickle 序列化的。

5.本地内存缓存

如果你的配置文件中没有指定其他缓存,那么这是默认的缓存。如果你想获得内存缓存的速度优势,但又不具备运行 Memcached 的能力,可以考虑使用本地内存缓存后端。这个缓存是每进程所有(见下文)和线程安全的。要使用它,可以将 BACKEND 设置为 "django.core.cache.backends.locmem.LocMemCache"。例如:

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

LOCATION 被用于标识各个内存存储。如果只有一个 locmem 缓存,你可以忽略 LOCATION 。但是如果你有多个本地内存缓存,那么你至少要为其中一个起个名字,以便将它们区分开。

这种缓存使用最近最少使用(LRU)的淘汰策略。

请注意,每个进程都会有自己的私有缓存实例,这意味着不可能进行跨进程缓存。这也意味着本地内存缓存的内存效率不是特别高,所以对于生产环境来说,它可能不是一个好的选择。对于开发来说是不错的选择。

6.虚拟缓存(用于开发模式)

最后,Django 带有一个实际上不是缓存的 “虚拟” 缓存,它只是实现缓存接口,并不做其他操作。

如果你有一个生产网站,在不同的地方使用了大量的缓存,但在开发/测试环境中,你不想缓存,也不想单独修改你的代码,那么这就很有用。要激活虚拟缓存,可以像这样设置 BACKEND :

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

7.使用自定义缓存后端

虽然 Django 包含了许多开箱即用的缓存后端支持,但有时你可能会想使用一个自定义的缓存后端。要在 Django 中使用外部缓存后端,使用 Python 导入路径作为 BACKEND 的 CACHES 配置中的 BACKEND,像这样:

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

如果你正在创建自己的后端,你可以使用标准缓存作为参考实现。你可以在 Django 源代码的 django/core/cache/backends/ 目录找到代码。

注意:除非是令人信服的理由,诸如服务器不支持缓存,否则你应该使用 Django 附带的缓存后端。他们经过了良好的测试并有完整文档。

8.缓存参数

每个缓存后端可以通过额外的参数来控制缓存行为。这些参数在 CACHES 配置中作为附加键提供。有效参数如下:

  • TIMEOUT :缓存的默认超时时间,以秒为单位。这个参数默认为 300 秒(5 分钟)。你可以将 TIMEOUT 设置为 None,这样,默认情况下,缓存键永远不会过期。值为 0 会导致键立即过期(实际上是 “不缓存”)。

  • OPTIONS :任何应该传递给缓存后端的选项。有效的选项列表会随着每个后端而变化,由第三方库支持的缓存后端会直接将其选项传递给底层缓存库。

    实施自有缓存策略的缓存后端(即 locmemfilesystem 和 database 后端)将尊重以下选项:

    • MAX_ENTRIES :删除旧值之前允许缓存的最大条目。默认是 300 。

    • CULL_FREQUENCY :当达到 MAX_ENTRIES 时,被删除的条目的比例。实际比例是 1 / CULL_FREQUENCY,所以将 CULL_FREQUENCY 设置为 2,即当达到 MAX_ENTRIES 时将删除一半的条目。这个参数应该是一个整数,默认为 3

      CULL_FREQUENCY 的值为 0 意味着当达到 MAX_ENTRIES 时,整个缓存将被转储。在某些后端(特别是 database ),这使得缓存速度  快,但代价是缓存未命中更多。

    The Memcached and Redis backends pass the contents of OPTIONS as keyword arguments to the client constructors, allowing for more advanced control of client behavior. For example usage, see below.

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

下面是一个基于 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,
            }
        }
    }
}

下面是一个基于 pymemcache 的后端配置实例,它启用了客户端池(通过保持客户端连接来提高性能),将 memcache/网络错误视为缓存失效,并在连接的 socket 上设置了 TCP_NODELAY 标志:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache',
        'LOCATION': '127.0.0.1:11211',
        'OPTIONS': {
            'no_delay': True,
            'ignore_exc': True,
            'max_pool_size': 4,
            'use_pooling': True,
        }
    }
}

Here's an example configuration for a redis based backend that selects database 10 (by default Redis ships with 16 logical databases), specifies a parser class (redis.connection.HiredisParser will be used by default if the hiredis-py package is installed), and sets a custom connection pool class (redis.ConnectionPool is used by default):

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.redis.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379',
        'OPTIONS': {
            'db': '10',
            'parser_class': 'redis.connection.PythonParser',
            'pool_class': 'redis.BlockingConnectionPool',
        }
    }
}

(四)站点缓存

一旦缓存设置完毕,使用缓存最简便的方式就是缓存整个站点。你需要在 MIDDLEWARE 设置中添加 'django.middleware.cache.UpdateCacheMiddleware' 和 'django.middleware.cache.FetchFromCacheMiddleware' ,像下面这个例子一样:

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

备注

不,这不是错别字:“update” 中间件必须在列表的第一位,而 “fetch” 中间件必须在最后。细节有点晦涩难懂,但如果你想知道完整的故事,请看下面的 中间件的顺序 。

最后,在 Django 设置文件里添加下面的必需配置:

  • CACHE_MIDDLEWARE_ALIAS -- 用于存储的缓存别名。
  • CACHE_MIDDLEWARE_SECONDS -- 应缓存每个页面的秒数。
  • CACHE_MIDDLEWARE_KEY_PREFIX -- 如果使用相同的 Django installation ,通过多站点进行缓存共享,请将此值设置为站点名,或者设置成在Django 实例中唯一的其他字符串,以此防止键冲突。如果你不介意,可以设置成空字符串。

在请求和响应标头允许的情况下,FetchFromCacheMiddleware 缓存状态为200的 GET 和 HEAD 响应。对于具有不同查询参数的相同URL的请求的响应被认为是单独的页面,并分别缓存。这个中间件期望一个HEAD请求的响应头与相应的GET请求具有相同的响应头;在这种情况下,它可以为HEAD请求返回一个缓存的GET响应。

此外,UpdateCacheMiddleware 在每个 HttpResponse 里会自动设置一些 headers,这会影响 下游缓存:

查看 中间件 获取更多中间件信息。

如果一个视图设置了它自己的缓存过期时间(比如在它的 Cache-Control header 里有 max-age 部分),然后页面将被缓存起来直到过期,而不是 CACHE_MIDDLEWARE_SECONDS 。使用在 django.views.decorators.cache 的装饰器,你可以很轻松的设置视图的过期时间(使用 cache_control() 装饰器)或者禁用视图缓存(使用 never_cache() 装饰器)。有关这些装饰器的更多信息,请查看 using other headers 部分。

如果设置 USE_I18N 为 True,然后已生成的缓存键将包含动态 language 的名称(参阅 Django 如何发现语言偏好)。这将允许你轻松缓存使用多语言的站点,而不用再创建缓存键。

当 USE_TZ 被设置为 True 时,缓存键也包括 当前时区

(五)视图缓存

django.views.decorators.cache.cache_page(timeout*cache=Nonekey_prefix=None)

使用缓存框架的通用办法是缓存视图结果。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 中指定视图缓存

上一节的例子硬编码了视图被缓存的事实,因为 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)),
]

(七)模板片段缓存

如果你获得更多的控制,你也可以使用 cache 模板标签(tag)来缓存模板片段。要使你的模板能够访问这个标签,请将 {% load cache %} 放在模板顶部。

{% cache %} 模板标签在给定的时间里缓存片段内容。它需要至少两个参数:缓存时效时间(以秒为单位),缓存片段的名称。如果缓存失效时间被设置为 None ,那么片段将被永久缓存。名称不能使变量名。例如:

{% load cache %}
{% cache 500 sidebar %}
    .. sidebar ..
{% endcache %}

有时你想缓存片段的多个副本,这取决于显示在的片段内一些动态数据。比如,你可能想为你的站点内每个用户分别独立缓存上面例子中的使用的 sidebar 副本。通过传递一个或多个附加参数,参数可能是带有或不带过滤器的变量,{% cache %} 模板标签必须在缓存片断中被唯一识别:

{% load cache %}
{% cache 500 sidebar request.user.username %}
    .. sidebar for logged in user ..
{% endcache %}

如果 USE_I18N 被设为 True,那么站点中间件缓存将支持多语言( respect the active language )。对于 cache 模板标签来说,你可以使用模板中可用的特定翻译变量之一( translation-specific variables )来达到同样的结果:

{% load i18n %}
{% load cache %}

{% get_current_language as LANGUAGE_CODE %}

{% cache 600 welcome LANGUAGE_CODE %}
    {% translate "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_namevary_on=None)

如果你想获得用于缓存片段的缓存键,你可以使用 make_template_fragment_key 。fragment_name 是 cache 模板标签的第二个参数;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 %}
>>> key = make_template_fragment_key('sidebar', [username])
>>> cache.delete(key) # invalidates cached template fragment
True

(八)底层缓存 API

有时,缓存整个渲染页面并不会带来太多好处,事实上,这样会很不方便。

或许,你的站点包含了一个视图,它的结果依赖于许多费时的查询,而且结果会随着时间变化而改变。在这个情况下,使用站点或视图缓存策略提供的全页面缓存并不理想,因为不能缓存所有结果(一些数据经常变动),不过你仍然可以缓存几乎没有变化的结果。

像这样的情况,Django 公开了一个底层的缓存 API 。你可以使用这个 API 以任意级别粒度在缓存中存储对象。你可以缓存任何可以安全的 pickle 的 Python 对象:模型对象的字符串、字典、列表,或者其他。(大部分通用的 Python 对象都可以被 pickle;可以参考 Python 文档关于 pickling 的信息)

(九)访问缓存

django.core.cache.caches

你可以通过类似字典一样的 object: django.core.cache.caches 对象访问在 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

这个对象等价于 caches['default'] 。

四、基本用法

基本接口是:

cache.set(keyvaluetimeout=DEFAULT_TIMEOUTversion=None)

>>> cache.set('my_key', 'hello, world!', 30)

cache.get(keydefault=Noneversion=None)

>>> cache.get('my_key')
'hello, world!'

key 是一个字符串,value 可以任何 picklable 形式的 Python 对象。

timeout 参数是可选的,默认为 CACHES 中相应后端的 timeout 参数。它是值存在缓存里的秒数。timeout 设置为 None 时将永久缓存。timeout 为0将不缓存值。

如果对象不在缓存中,cache.get() 将返回 None

>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key')
None

如果你需要确定对象是否存在于缓存中,并且你已经存储了一个字面值 None,使用一个前哨对象作为默认:

>>> sentinel = object()
>>> cache.get('my_key', sentinel) is sentinel
False
>>> # Wait 30 seconds for 'my_key' to expire...
>>> cache.get('my_key', sentinel) is sentinel
True

MemcachedCache

由于 python-memcached 的限制,在已废弃的 MemcachedCache 后端,不可能区分存储的 None 值和返回值为 None 的缓存失效。

cache.get() 可以带一个默认参数。如果对象不在缓存中,将返回指定的值。

>>> cache.get('my_key', 'has expired')
'has expired'

cache.add(keyvaluetimeout=DEFAULT_TIMEOUTversion=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(keydefaulttimeout=DEFAULT_TIMEOUTversion=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(keysversion=None)

这里也有 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(dicttimeout)

使用 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() 带有一个可选的 timeout 参数。

在已支持的后端(memcached),set_many() 会返回无法插入的键列表。

cache.delete(keyversion=None)

你可以使用 delete() 显示地删除键,以清空特定对象的缓存:

>>> cache.delete('a')
True

如果键被成功删除,将返回 delete() ,否则返回 False 。

cache.delete_many(keysversion=None)

如果你想一次性清除很多键,给 delete_many() 传递一个键列表即可删除。

>>> cache.delete_many(['a', 'b', 'c'])

cache.clear()

最后,如果你想删除缓存里的所有键,使用 cache.clear()。注意,clear() 将删除缓存里的 任何 键,不只是你应用里设置的那些键。

>>> cache.clear()

cache.touch(keytimeout=DEFAULT_TIMEOUTversion=None)

cache.touch() 为键设置一个新的过期时间。比如,更新一个键为从现在起10秒钟后过期:

>>> cache.touch('a', 10)
True

和其他方法一样,timeout 参数是可选的,并且默认是 CACHES 设置的相应后端的 TIMEOUT 选项。

如果键被成功 touch(),将返回 True,否则返回 False

cache.incr(keydelta=1version=None)

cache.decr(keydelta=1version=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

备注

不保证 incr() / decr() 方法是原子。那些后端支持原子递增/递减(最值得注意的是 memcached 后端),递增和递减操作是原子的。然而,如果后端本身没有提供递增/递减方法,则将使用两步(检索和更新)来实现。

cache.close()

如果缓存后端已经实现了 close() 方法,你可以关闭和缓存的连接。

>>> cache.close()

备注

对于没有实现 close 方法的缓存,它将无效操作。

备注

The async variants of base methods are prefixed with a, e.g. cache.aadd() or cache.adelete_many(). See Asynchronous support for more details.

Changed in Django 4.0:

The async variants of methods were added to the BaseCache.

1.缓存键前缀

如果你正在服务器之间或者生产/开发缓存之间共享缓存实例,有可能会使得一个服务器使用另一个服务器的缓存数据。如果缓存数据格式是相同的,这会导致一些难以诊断的问题。

为了防止这个问题,Django 为单台服务器提供了为所有缓存键提供前缀的方法。当一个特殊的缓存键被保存或检索时,Django 会为缓存键自动添加 KEY_PREFIX 缓存设置的前缀值。

要确保每个 Django 实例有不同的 KEY_PREFIX ,这样就保证缓存值不会发生冲突。

2.缓存版本控制

当更改使用缓存值的运行代码时,你可能需要清除任何已存的缓存值。最简单的方法是刷新整个缓存,但这会导致那些仍然有用且有效的缓存值。

Django 提供更好的方式来指向单个缓存值。Django 缓存框架有一个系统范围的版本标识,需要在 VERSION 缓存配置中指定。这个配置的值将自动与缓存前缀和用户提供的缓存键组合起来获取最终的缓存键。

默认情况下,任何键请求将自动包含站点默认缓存键版本。但是,早期的缓存函数都包含一个 version 参数,因此你可以指定 set 还是 get 特定缓存键的版本。举例:

>>> # 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!'

3.缓存键转换

如前面两节所述,用户提供的缓存键不是单独使用的,它是与缓存前缀和键版本组合后获取最终缓存键。默认情况下,使用冒号连接这三部分生成最终的字符串:

def make_key(key, key_prefix, version):
    return '%s:%s:%s' % (key_prefix, version, key)

如果你想用不同方式组合,或者应用其他处理来获得最终键(比如,获得关键部分的哈希摘要),那么你可以提供一个自定义的键函数。

KEY_FUNCTION 缓存设置指定一个与上面的 make_key() 原型匹配的函数路径。如果提供,这个自定义键函数将代替默认的键组合函数来使用。

4.缓存键警告

Memcached 作为最常用的缓存后端,不允许缓存键超过250个字符、包含空格或控制字符,并且使用这些键将会导致异常。为了增加代码可移植性和最小惊讶,如果使用会导致 memcached 报错的键,那么其他内置的缓存框架会发出警告( django.core.cache.backends.base.CacheKeyWarning )。

如果你正在使用的生产后端能接受更大范围的键(自定义后端或非 memcached 的内置后端),并且在没有警告的情况下使用更广的范围,你可以在 INSTALLED_APPS 中的 management 模块里静默 CacheKeyWarning 使用这个代码:

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 部分使用路径导入此类。

五、异步支持

New in Django 4.0.

Django has developing support for asynchronous cache backends, but does not yet support asynchronous caching. It will be coming in a future release.

django.core.cache.backends.base.BaseCache has async variants of all base methods. By convention, the asynchronous versions of all methods are prefixed with a. By default, the arguments for both variants are the same:

>>> await cache.aset('num', 1)
>>> await cache.ahas_key('num')
True

六、注意事项

1.全站缓存时文章的更新

启用缓存之后网站的访问速度是提升,但是同样的在新建页面的时候,比如说新增一篇文章,网站页面不会立即显示这篇文章,因为首页的内容已经被放到了缓存中,当你再次访问的时候,Django不是从数据库中捞数据,而是从缓存中捞数据。解决方法就是给函数前设置一个页面缓存。

from django.views.decorators.cache import cache_page
@cache_page(60) # 60代表缓存的寿命,表示缓存时长60秒钟
def memcached(req):
    return render(req,"memcached.html")

 2.post请求是更新缓存

参考链接:django 自带页面缓存cache_page的使用及清除_果汁华的博客-CSDN博客

from django.core.cache import cache
from django.utils.cache import get_cache_key

def expire_page(requset):
    key = get_cache_key(request)
    print(key )
    #判断缓存中是否存在
    if cache.has_key(key):
        cache.delete(key)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Django缓存实现】前端缓存和后端缓存 的相关文章

随机推荐

  • yaml 数组_YAML配置文件简介及使用

    简介 YAML 是 YAML Ain t a Markup Language YAML 不是一种标记语言 的缩写 相比JSON格式的方便 语法 内容是大小写敏感 使用缩进表示层级关系 有点像Python缩进 缩进只允许空格 不能使用tab代
  • 类中包含几种成员,分别是什么?

    类中有两种成员 成员变量以及成员函数 其中成员变量分为静态的成员变量以及非静态的成员变量 成员函数分为静态的成员函数以及非静态的成员函数
  • 在Java中使用redisTemplate操作缓存

    在最近的项目中 有一个需求是对一个很大的数据库进行查询 数据量大概在几千万条 但同时对查询速度的要求也比较高 这个数据库之前在没有使用Presto的情况下 使用的是Hive 使用Hive进行一个简单的查询 速度可能在几分钟 当然几分钟也并不
  • linux下定位内存泄漏 /proc/pid/status 解释

    内存泄漏一直是程序定位的盲点 很多时候感觉用着用着内存会越来越少 导致程序崩溃 而一般top等linux命令又不够详细 通过cat proc pid status 命令 可详细查看进程的内存占用情况 其中pid是进程id 进程号去查状态 c
  • Java:线程的三种中断方式

    文章目录 前言 一 线程的Stop 操作 二 线程的Interrupt 方法进行中断操作 1 stop 方法的缺点 2 Interrupt 方法 三 使用run标志位进行判断 总结 前言 在 Java 中 并发机制非常重要 但并不是所有程序
  • 分库分表的概念

    目录 一 分库分表有什么用 二 分库分表的方式 三 分库分表的缺点 四 什么时候需要分库分表 五 常见的分库分表组件 总结 在前面写了一篇关于MySQL主从集群的文章 而主从的作用 在我们开发角度更大的作用是作为读写分离的支持 也是学习Sh
  • Debian系统下network和NetworkManager冲突及关闭NetworkManager

    在Debian Linux下 network服务管理对于网卡的配置 NetworkManager是由管理系统网络链接的服务和允许用户管理网络连接的客户端服务组成 network和NetworkManager服务会出现冲突 一般如果想另外使用
  • [前端系列第7弹]Vue:一个渐进式的 JavaScript 框架

    Vue 是一个用于构建用户界面的 JavaScript 框架 它具有以下特点 渐进式 Vue 可以根据不同的使用场景 灵活地选择使用库或者框架的方式 从而实现渐进式的开发 响应式 Vue 通过数据绑定和虚拟 DOM 技术 实现了高效的响应式
  • ajax数字的正则表达式,validateform正则表达式 datatype验证数字

    第8章 用户模式下的线程同步 4 lowbar 条件变量 Condition Variable 8 6 条件变量 Condition Variables 可利用临界区或SRWLock锁来实现 8 6 1 条件变量的使用 1 条件变量机制就是
  • BigDecimal转化为String

    Oracle Java字段类型转换 从数据库取出一个字段 在java中为BigDecimal类型 将其转化为String类型的字段时 报转化异常的错误java math BigDecimal cannot be cast to java l
  • Spring面试题整理

    Spring的优缺点是什么 优点 1 方便解耦 简化开发 Spring就是一个大工厂 可以将所有对象的创建和依赖关系的维护 交给Spring管理 2 AOP编程的支持 Spring提供面向切面编程 可以方便的实现对程序进行权限拦截 运行监控
  • WRF系列教程1:WRF如何得到更好的模拟结果?

    编者按 这是新开的一个系列 有时间会逐步将WRF官方培训的ppt挑选个人认为重要的进行翻译 以及结合个人的使用经验进行一些解释 由于个人水平有限 难免会出现偏差和错误 欢迎斧正 本篇内容来源于WRF官网2021年的培训ppt Applica
  • 如何用frp做内网穿透

    使用场景 需要将内网的一些应用端口开放出来 以便可以通过外网访问或者第三方调试使用 采用工具 frp 0 28 0 linux amd64 tar gz 工具下载地址 https github com fatedier frp releas
  • element 实现表格滚动vue-seamless-scroll --save

    npm install vue seamless scroll save main js import scroll from vue seamless scroll Vue use scroll
  • 服务器扩容 --挂载磁盘方式(学习笔记)

    一 查看服务器磁盘 df h fdisk l 可以看到新增加了一块硬盘 dev sdb 大概有4T的容量 二 挂载磁盘 1 进行磁盘分区 fdisk dev sdb sdb为新加磁盘名称 步骤如下 2 查看新建分区 fdisk l 3 对新
  • create connection SQLException, url: jdbc:mysql://localhost:3306/users?characterEncoding=utf-8, erro

    今天写JDBCTemplate的时候出现bug 一开始网上查的时候说可能是驱动版本和数据库版本不太对 但是后来手写连接用DriverManager获取连接是可以获取得到的 然后又用Druid连接池试了一下 也可以获取连接 所以排除这个问题
  • BP脑电数据处理

    BP Brain Products 脑电数据处理 一 BP分析软件导出数据 标签 1 1 BP分析软件加载原始数据 1 2导出Markers 1 3 将原始数据导出成edf格式输出 1 4 MATLAB处理 一 BP分析软件导出数据 标签
  • [机缘参悟-92]:《本质思考》- 本质思考的9种训练方法

    目录 前言 01 假设力 尽可能涵盖所有的可能方案 02 逆向思考力 从未来可能的失败倒推 03 共情力 不断地站在他人的角度看问题 04 信息整理力 辨别每种信息的类型和属性和背后意图 05 图像化能力 掌握更直观的表达方式 06 定规则
  • 电子电路图中VCC、IO、3V3OUT、VDD3V3解释

    1 Vcc 一般表示电源正端 是晶体管集电极或IC集电极供电电压 2 IO 输入 输出端口 3 3V3OUT 3 3V输出端 4 VDD 一般表示电源正端 是场效应管漏极或IC内漏极供电电压 5 3V3 3 3V端 一般是供电电压为3 3V
  • 【Django缓存实现】前端缓存和后端缓存

    目录 一 什么是缓存 二 Web缓存 一 前端缓存 二 后端缓存 三 Django缓存 一 缓存类型 二 设置缓存 1 Memcached 2 Redis 3 数据库缓存 4 文件系统缓存 5 本地内存缓存 6 虚拟缓存 用于开发模式 7