codecamp

Django4.0 缓存框架-设置缓存

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

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

Memcached ​是一个完全基于内存的缓存服务器,最初开发用于处理 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',
        ]
    }
}

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

Redis

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

设置 Redis 服务器后,您需要为 Redis 安装 Python 绑定。 redis-py 是 Django 原生支持的绑定。 还建议安装额外的hiredis-py 包。

使用 Redis 作为 Django 的缓存后端:

  • 将 ​BACKEND ​设置为 ​django.core.cache.backends.redis.RedisCache
  • 使用适当的方案将 ​LOCATION ​设置为指向您的 Redis 实例的 URL。

例如,如果 Redis 在 localhost (127.0.0.1) 端口 6379 上运行:

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

数据库缓存

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

与其他缓存后端不同,数据库缓存不支持在数据库级别自动剔除过期条目。 相反,每次调用 ​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

如果你没有指定路由指向数据库缓存模型,缓存后端将使用 默认 的数据库。
如果没使用数据库缓存后端,则无需担心为数据库缓存模型提供路由指令。

文件系统缓存

基于文件的后端序列化并保存每个缓存值作为单独的文件。要使用此后端,可将 ​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 ​可读写,或者它可以由用户 ​apache ​创建。

当缓存 ​LOCATION ​包含在 ​MEDIA_ROOT ​或 ​STATICFILES_FINDERS ​中,敏感数据可能被暴露。
获得访问缓存文件的攻击者不仅可以伪造 HTML 内容,你的网站会信任它,而且还可以远程执行任意代码,因为数据是用 ​pickle ​序列化的。

本地内存缓存

如果你的配置文件中没有指定其他缓存,那么这是默认的缓存。如果你想获得内存缓存的速度优势,但又不具备运行 ​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 导入路径作为 ​BACKEND ​的 ​CACHES ​配置中的 ​BACKEND​,像这样:

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

如果你正在创建自己的后端,你可以使用标准缓存作为参考实现。你可以在 Django 源代码的 ​django/core/cache/backends/​ 目录找到代码。
注意:除非是令人信服的理由,诸如服务器不支持缓存,否则你应该使用 Django 附带的缓存后端。他们经过了良好的测试并有完整文档。

缓存参数

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

  • TIMEOUT ​:缓存的默认超时时间,以秒为单位。这个参数默认为 300 秒(5 分钟)。你可以将 ​TIMEOUT ​设置为 ​None​,这样,默认情况下,缓存键永远不会过期。值为 0 会导致键立即过期(实际上是 “不缓存”)。
  • OPTIONS ​:任何应该传递给缓存后端的选项。有效的选项列表会随着每个后端而变化,由第三方库支持的缓存后端会直接将其选项传递给底层缓存库。

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

  • MAX_ENTRIES ​:删除旧值之前允许缓存的最大条目。默认是 300 。
  • CULL_FREQUENCY ​:当达到 ​MAX_ENTRIES ​时,被删除的条目的比例。实际比例是 1 / CULL_FREQUENCY,所以将 ​CULL_FREQUENCY ​设置为 2,即当达到 ​MAX_ENTRIES ​时将删除一半的条目。这个参数应该是一个整数,默认为 3。
  • CULL_FREQUENCY ​的值为 0 意味着当达到 ​MAX_ENTRIES ​时,整个缓存将被转储。在某些后端(特别是 ​database ​),这使得缓存速度更快,但代价是缓存未命中更多。

Memcached 和 Redis 后端将 ​OPTIONS ​的内容作为关键字参数传递给客户端构造函数,从而允许对客户端行为进行更高级的控制。 

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

这是一个基于 redis 的后端的示例配置,它选择数据库 10(默认情况下 Redis 附带 16 个逻辑数据库),指定解析器类(如果安装了​hiredis-py​ 包,则默认使用 ​redis.connection.HiredisParser​),以及 设置自定义连接池类(默认使用​redis.ConnectionPool​):

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


Django4.0 进阶测试主题-集成 coverage.py
Django4.0 缓存框架-站点缓存
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

Django4.0 模型和数据库

Django4.0 处理HTTP请求

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }