缓存系统需要少量的设置。也就是说,你必须告诉它你的缓存数据应该放在哪里 —— 是在数据库中,还是在文件系统上,或者直接放在内存中。这是一个重要的决定,会影响你的缓存的性能;是的,有些缓存类型比其他类型快。
缓存设置项位于你的配置文件的缓存配置中。这里有缓存配置所有可用值的说明。
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 安装 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',
}
}
}