Django 缓存框架

动态网站的一个基本权衡是它们是动态的。每当用户请求页面时,Web 服务器进行各种计算,从数据库查询到模板渲染到业务逻辑,以创建您网站访问者看到的页面。从处理开销的角度来看,这比标准的从文件系统中读取文件的服务器安排要昂贵得多。

对于大多数 Web 应用程序,这种开销并不是什么大问题。大多数 Web 应用程序不像 washingtonpost.comslashdot.org 那样大型,它们是小到中等规模的站点,流量一般。但对于中等到高流量的站点来说,尽量减少开销是非常重要的。

这就是缓存的用武之地。

缓存是指保存昂贵计算的结果,以便下次无需再次执行计算。以下是一些伪代码,说明了如何为动态生成的网页实现缓存:

复制代码
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

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

Django 也与 "下游" 缓存很好地配合,​ 比如 Squid 和基于浏览器的缓存。这些是您不直接控制但可以通过 HTTP 标头提供提示的缓存类型,用于指示哪些部分的网站应该被缓存,以及如何缓存。

一、设置缓存

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

你的缓存首选项在设置文件中的 CACHES 设置中。以下是所有可用值对 CACHES 的解释。

1、Memcached

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

默认情况下,PyMemcacheCache 后端设置以下选项(您可以在 OPTIONS 中覆盖它们):

复制代码
"OPTIONS": {
    "allow_unicode_keys": True,
    "default_noreply": False,
    "serde": pymemcache.serde.pickle_serde,
}

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

2、Redis

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

在设置好 Redis 服务器之后,您需要安装 Redis 的 Python 绑定。Django 原生支持的绑定是 redis-py。同时,建议安装 hiredis-py 包。

要在 Django 中使用 Redis 作为缓存后端:

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

例如,如果 Redis 在本地主机(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:[email protected]: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
        ],
    }
}

from django.core.cache import cache

# 设置键值对(默认使用配置中的 TIMEOUT)
cache.set("user:1:name", "Alice")

# 设置键值对并指定超时时间(单位:秒)
cache.set("user:1:email", "[email protected]", timeout=3600)

# 获取单个值
name = cache.get("user:1:name")  # 返回 "Alice"
email = cache.get("user:1:email")  # 返回 "[email protected]"

# 获取不存在的键(返回 None)
age = cache.get("user:1:age")  # 返回 None

# 设置默认值(如果键不存在)
age = cache.get("user:1:age", default=25)  # 返回 25

3、数据库缓存

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

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

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

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

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

确保该设置所指向的目录存在,并且具备读写权限,或者可以由您的 Web 服务器运行的系统用户创建。继续上面的示例,如果您的服务器以用户 apache 运行,请确保目录 /var/tmp/django_cache 存在,并且用户 apache 具备读写权限,或者可以由用户 apache 创建。

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)的淘汰策略。

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

相关推荐
Themberfue2 小时前
Redis ⑦-set | Zset
java·开发语言·数据库·redis·sql·缓存
hi星尘4 小时前
深度解析:基于Python的微信小程序自动化操作实现
python·微信小程序·自动化
miracletiger6 小时前
uv 新的包管理工具总结
linux·人工智能·python
我不会编程5557 小时前
Python Cookbook-6.10 保留对被绑定方法的引用且支持垃圾回收
开发语言·python
ʚɞ 短腿欧尼7 小时前
关系数据的可视化
python·pycharm·可视化·数据可视化·图表
八股文领域大手子7 小时前
深入理解缓存淘汰策略:LRU 与 LFU 算法详解及 Java 实现
java·数据库·算法·缓存·mybatis·哈希算法
noravinsc8 小时前
django admin 中更新表数据 之后再将数据返回管理界面
数据库·django·sqlite
Bruce-li__9 小时前
DRF凭什么更高效?Django原生API与DRF框架开发对比解析
数据库·django·sqlite
PXM的算法星球9 小时前
【软件工程】面向对象编程(OOP)概念详解
java·python·软件工程