Django缓存
一 缓存
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者memcache中之前缓存的内容拿到,并返回。
Django缓存框架的管档,写的很好,非常值得一读:https://docs.djangoproject.com/en/3.2/topics/cache/
Django缓存框架的核心目标:https://docs.djangoproject.com/en/3.2/misc/design-philosophies/#cache-design-philosophy
缓存的好处无需多言,所以Django提供了专门的缓存框架来支持各种缓存:
- 不同的缓存粒度,推荐程度由高到低排序:
- 指定视图缓存,粒度适中。
- 模板片段缓存,对模板文件的局部内容进行缓存,粒度较细。
- 全站缓存,粒度最大。
- 更低级别的缓存API,用来缓存Python中的对象(list、dict、模型对象...),没用过,但在某些情况下(我暂时还没想到)很有用的样子。
- 不同的缓存方式,推荐程度由高到低排序:
- 第三方缓存数据库(redis、Memcache),专业的才是最好的嘛。另外,随着内存数据库的发展演变,Memcache不如redis用的多了。
- 本地文件缓存,将渲染好的字符串缓存到文件中,下次查询省了渲染的步骤了,内存不大的小站点非常有用。
- 本地数据库缓存,套路同本地文件缓存。
- 本地内存缓存,内存大了啥都好说。
- 虚拟缓存,用于开发,Django 带有一个实际上并不缓存的“虚拟”缓存——它只是实现了缓存接口而不做任何事情。
Django中提供了6种缓存方式:
- 开发调试
- 内存
- 文件
- 数据库
- Memcache缓存(python-memcached模块)
- Memcache缓存(pylibmc模块)
缓存参数
这里来列一下公共的缓存参数:
TIMEOUT
:用于缓存的默认超时,以秒为单位。此参数默认为300
秒(5 分钟)。您可以设置TIMEOUT
为None
,默认情况下,缓存键永不过期。值0
会导致键立即过期(实际上是“不缓存”)。OPTIONS
:应该传递给缓存后端的任何选项。每个后端的有效选项列表会有所不同,第三方库支持的缓存后端会将其选项直接传递给底层缓存库。MAX_ENTRIES
:删除旧值之前缓存中允许的最大条目数。此参数默认为300
。CULL_FREQUENCY
:当缓存条目达到MAX_ENTRIES
时,剔除缓存中的比例,这个值必须是整数,默认是3,即剔除三分之一,如果是即剔除一半。存疑的是剔除的策略,我只查到了基于本地内存的缓存剔除策略是LRU,其它的貌似都是随机剔除。
KEY_PREFIX
: 一个字符串,将自动包含(默认添加)到 Django 服务器使用的所有缓存键中。VERSION
:Django 服务器生成的缓存键的默认版本号。KEY_FUNCTION
一个字符串,其中包含一个函数的虚线路径,该函数定义了如何将前缀、版本和密钥组合成最终的缓存密钥。
redis
现在redis数据库用的较多,所以放到最上面!但是django的缓存框架中并没有实现.....但django-redis
模块实现了,所以:
先下载django-redis
模块:
pip install django-redis
然后在settings.py
中进行配置:
CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", # "LOCATION": "redis://IP:PORT/可选的数据库参数", # "LOCATION": "redis://127.0.0.1:6379/0", "LOCATION": "redis://127.0.0.1:6379", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "CONNECTION_POOL_KWARGS": { "max_connections": 100 }, "DECODE_RESPONSES": True, # True返回的数据自动decode "PASSWORD": "", # 访问redis是否需要密码 } } }
1、配置
a、虚拟缓存(开发调试)
项目中使用了缓存,但在开发和调试时,则希望跳过缓存,那么这个虚拟缓存就有用了,你只需要在配置文件中进行配置即可:
# 此为开始调试用,实际内部不做任何操作 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) }, 'KEY_PREFIX': '', # 缓存key的前缀(默认空) 'VERSION': 1, # 缓存key的版本(默认1) 'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】) } } # 自定义key def default_key_func(key, key_prefix, version): """ Default function to generate keys. Constructs the key used by all other methods. By default it prepends the `key_prefix'. KEY_FUNCTION can be used to specify an alternate function with custom key making behavior. """ return '%s:%s:%s' % (key_prefix, version, key) def get_key_func(key_func): """ Function to decide which key function to use. Defaults to ``default_key_func``. """ if key_func is not None: if callable(key_func): return key_func else: return import_string(key_func) return default_key_func
b、本地内存(django默认配置)
# 此缓存将内容保存至内存的变量中 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', #这是一个唯一标示,写啥都行
'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
'OPTIONS':{
'MAX_ENTRIES': 300, # 最大缓存个数(默认300)
'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
},
} }
# 注:其他配置同开发调试版本
c、文件
# 此缓存将内容保存至文件,比如已经经过模版渲染完之后的html文件内容,如果缓存到文件中,以后就不需要再去进行模版渲染了,直接拿缓存中的要快一些 # settings配置文件中写上以下配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/var/tmp/django_cache', #缓存文件存放路径 } } # 注:其他配置同开发调试版本
目录路径应该是绝对路径——也就是说,它应该从文件系统的根目录开始。是否在设置的末尾加上斜线并不重要。
确保此设置指向的目录存在并且可读可写,或者它可以由运行 Web 服务器的系统用户创建。
d、数据库
# 此缓存将内容保存至数据库 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', # 数据库表 } } # 注:执行创建表命令 python manage.py createcachetable,数据库中会自动生成名字为my_cache_table的表作为缓存表
注意:如果我们自己手动创建这个缓存表的时候,数据库表的三个字段是必须要有的:看下面的表,并且不需要制定上面的createcachetable指令就可以使用。
from django.db import models
# Create your models here.
class CacheTalbe(models.Model):
cache_key = models.CharField(max_length=2000)
value = models.CharField(max_length=2000,null=True)
expires = models.DateTimeField(null=True)
与其他缓存后端不同,数据库缓存不支持在数据库级别自动剔除过期条目。相反,每次调用add()
、set()
或时都会剔除过期的缓存条目。
完事之后,就要创建缓存表了:
python manage.py createcachetable
这将在您的数据库中创建一个表格,该表格具有 Django 的数据库缓存系统所期望的正确格式。表名取自 LOCATION
。
如果是多数据库的话,需要单独配置,参考管档吧:https://docs.djangoproject.com/en/3.2/topics/cache/#multiple-databases
e、Memcache缓存(python-memcached模块)
# 此缓存使用python-memcached模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': 'unix:/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }
f、Memcache缓存(pylibmc模块)
# 此缓存使用pylibmc模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }
2、应用
a. 全站使用
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存 缓存多长时间等配置默认是按照你上面的配置部分来的 MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', #中间件第一个 # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', #中间件最后一个 ] CACHE_MIDDLEWARE_ALIAS = "" 用于存储的缓存别名。 CACHE_MIDDLEWARE_SECONDS = "" 应该缓存每个页面的秒数 CACHE_MIDDLEWARE_KEY_PREFIX = "" 如果使用同一django安装跨多个站点共享缓存,请将其设置为站点名称或此django实例唯一的其他字符串,以防止密钥冲突。如果你不在乎,就用空字符串。
看图解
django默认配置为:
from django.conf import global_settings #在这个配置文件中我们能够看到 #以下配置是django缓存的默认配置,如果我们自己没有进行配置,那么下面的使用,默认会使用我们的默认配置,也就是本地内存 # The cache backends to use. CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', } } CACHE_MIDDLEWARE_KEY_PREFIX = '' CACHE_MIDDLEWARE_SECONDS = 600 CACHE_MIDDLEWARE_ALIAS = 'default'
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake', 'OPTIONS': { 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) }, } } # 默认潮湿时间是300秒,我们可以通过CACHE_MIDDLEWARE_SECONDS来修改 CACHE_MIDDLEWARE_SECONDS = 5 # 下面两个是关于key的,保持默认就完了 CACHE_MIDDLEWARE_KEY_PREFIX = "" CACHE_MIDDLEWARE_ALIAS = ""
b. 单独视图缓存(先看这个)
方式一: from django.views.decorators.cache import cache_page import time @cache_page(5) def my_view(request):
tm = time.time() ...
all_book = models.Book.objects.all()
print(all_book) #也是5秒之后打印一次,因为5秒内都是从缓存中拿的,没有执行我们的视图函数,所有没有去数据库中取,注意,对实时性数据要求很高的,不要做缓存
return render(request,'index.html',{'tm':tm }) # 当我们刷新页面的时候,你会发现tm在5秒之后才会发生变化 方式二: from django.views.decorators.cache import cache_page urlpatterns = [ url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)), #装饰器原始用法 ]
c、局部模板使用
a. 引入TemplateTag {% load cache %} b. 使用缓存 {% cache 5000 缓存key %}
{% cache 5000 'unique-snowflake' %}
缓存内容 {% endcache %}
局部使用示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>哈哈哈</h1> <h2>{{ tm }}</h2> {% load cache %} {% cache 5 'c1' %} <h2>{{ tm }}</h2> {% endcache %} </body> </html>
更多参考管档吧:https://docs.djangoproject.com/en/3.2/topics/cache/#template-fragment-caching
django-redis配置缓存详见文档: https://django-redis-chs.readthedocs.io/zh_CN/latest/
see also: https://www.cnblogs.com/wupeiqi/articles/5246483.html
更多:猛击这里
django-redis模块
django项目中想要使用redis作为缓存工具,则需要下载专门的模块django-redis来完成。
django-redis&&redis软件&&redis-py模块,他们之间的关系
python中直接操作redis软件,需要使用redis-py模块,而django-redis是对redis-py模块的基础上进行封装,从而方便在django项目中使用。
我们可以使用django-redis来完成:
- 结合django的session组件使用。
- 作为django的缓存工具。
install
# 之前如果有低版本的可以先卸载
pip uninstall django-redis -y
# 然后下载最新版的django-redis
pip install django-redis
# 我这里以5.3.0版本为例,不同的版本之间,可能略有差异,所以要版本问题要注意
pip install django-redis==5.3.0
提前说下小结吧
Python操作Redis,可以通过redis-py模块,这个是功能最全的,把这个掌握好就行。
Django项目中为了更方便的使用Redis,搞出来了django-redis模块,但这个模块毕竟是二次封装的,所以有些功能实现不了,也不用纠结,我们完全可以在Django中使用redis-py模块就完了。
作为 session backend 使用配置
在你的django的settings.py中,可以对django-redis进行相关配置,使其更好的支持django的session组件。
# session配置
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
# 设置session保存的位置对应的缓存配置项
SESSION_CACHE_ALIAS = 'session'
# 过期时间
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7
作为 cache backend 使用配置
基本的配置
为了使用 django-redis , 你应该将你的 django cache setting 改成这样:
# cache缓存
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:@127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "", # 密码,如果没有设置密码,这个参数可以注视掉
}
}
}
一个CACHES里可以配置多个redis连接对象,每一个都有自己的别名(alias),上面的"default"就是别名。
我们可以设置多个不同的连接对象,更细粒度的进行功能划分。
# cache缓存
CACHES = {
# 默认缓存
"default": {
"BACKEND": "django_redis.cache.RedisCache",
# 项目上线时,需要调整这里的路径
# "LOCATION": "redis://:密码@IP地址:端口/库编号",
"LOCATION": "redis://:@127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 10},
}
},
# 提供给admin运营站点的session存储
"session": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:@127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 10},
}
},
# 提供存储短信验证码
"sms_code":{
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:@127.0.0.1:6379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 10},
}
}
}
连接池
# cache缓存
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://www.neeo.cc:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
# 连接池的配置都在CONNECTION_POOL_KWARGS对应的字典中
"CONNECTION_POOL_KWARGS": {
"max_connections": 100,
"encoding": "utf-8",
"decode_responses": True,
"timeout": 5,
},
"PASSWORD": "",
}
}
}
Django的缓存对象cache
在Django中了redis作为缓存之后,我们可以在项目中通过调用Django提供的全局缓存对象cache,来做一些事情,不过这个对象能干的事情有限,这里仅作为了解即可。
# 这么导入Django提供的全局缓存对象cache,它默认读取的是settings文件中CACHES中的 'default' ,如果没没有配置default,这个cache默认是用不了的
from django.core.cache import cache
# 之所以说它能干的事情有限,是因为它真的不好用,我随手举两个例子,其它的姿势我没尝试,所以就看看就完了
# 设置键值对,同时也可以指定超时时间
# cache.set("k1", "v1", timeout=25)
# print(cache.get("k1"))
# print(cache.ttl("k1"))
"""
ttl返回值
0 表示 key 不存在
300 表示 key 存在但没有设置过期或者已经过期了
或者返回一个具体的超时时间
"""
# 如果一个key最开始没有设置超时时间,那么,我们可以手动通过expire给它设置一个超时时间
# cache.set("k2", "v2")
# cache.expire("k2", timeout=25)
# print(111, cache.ttl("k2"))
# 看着也能用,但是你用它搞点其它的,比如操作列表和集合......就报错了
# cache.sadd('s1', 'a', 'b', 'c', 1, 2, 3, 1) # AttributeError: 'RedisCache' object has no attribute 'sadd'. Did you mean: 'add'?
# print(cache.smembers('s1'))
# cache.lpush('l1', 1, 2, 3) # AttributeError: 'RedisCache' object has no attribute 'lpush'
# print(cache.lrange('l1', 0, -1))
我个人的建议时,如果使用redis作为Django的缓存,那么在项目中就不用这个Django默认的缓存对象cache。
使用原生Redis对象
既然,Django默认的缓存对象不好用,那么我们用啥呢?答案就是用原生的Redis连接对象。
基本使用
import os
import sys
import django
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_dir)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day06.settings')
django.setup() # 伪造让django启动
# 导包
from django_redis import get_redis_connection
# 先通过别名拿到Redis连接对象,get_redis_connection(alias)
# alias:可以是Django的settings中CACHES中配置的多个别名
# conn = get_redis_connection() # 不传值,默认使用的是default
conn = get_redis_connection('sms_code') # 或者显示的使用某个别名
# 最基本的用法,获取连接对象,并设置值,同时指定过期时间,单位: 秒
conn.set('18211101111', '8842', ex=10)
# 在需要的地方在通过连接对象获取值
print(conn.get("18211101111"))
使用管道
import os
import sys
import django
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_dir)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day06.settings')
django.setup() # 伪造让django启动
from django_redis import get_redis_connection
# 获取连接对象
conn = get_redis_connection('sms_code')
# 使用管道
pipe = conn.pipeline()
pipe.set('k1', 'v1')
pipe.set('k2', 'v2')
pipe.set('k3', 'v3')
res = pipe.execute()
print(res)
ttl相关
import os
import sys
import django
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_dir)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day06.settings')
django.setup() # 伪造让django启动
from django_redis import get_redis_connection
conn = get_redis_connection('default')
# 设置键值对的时候,同时指定过期时间
conn.set('k2', 'v2', ex=25)
print(conn.get("k2"))
print(conn.ttl("k2"))
"""
conn.ttl("k111")的返回值
如果返回 具体的数字 表示该key的剩余过期时间
如果返回 -2 表示key不存在
如果返回 -1 表示key存在,但没有设置过期时间
"""
# 如果一个key最开始没有设置超时时间,那么,我们可以手动通过expire给它设置一个超时时间
conn.set('k3', 'v3')
conn.expire('k3', 20) # 设置过期时间
print(conn.ttl("k3"))
conn.persist('k3') # 取消过期时间
print(conn.ttl("k3"))
其它用法
import os
import sys
import django
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_dir)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day06.settings')
django.setup() # 伪造让django启动
from django_redis import get_redis_connection
conn = get_redis_connection('default')
# 其它数据类型操作就按照redis-py模块中讲的那样操作就好了
# ------------------ hash ------------------
# data = {"id": 1, "user": "zhangkai", 'age': 18}
#
# # conn.hmset('user1', mapping=data) # 这种写法,有问题
# conn.hset(name='user2', key="name", value='zhangkai') # 这种写法没问题
# # conn.hset(name='user3', mapping=data) # 这种写法,有问题
# print(conn.hget('user2', 'name'))
# ------------------ list ------------------
# l1 = ['a', 'b', 'c', 'd']
# # conn.lpush('l1', 'a', 'b', 'c')
# # 批量插入列表
# conn.lpush('l1', *l1)
# print(conn.lrange('l1', 0, 2))
# ------------------ set ------------------
# # 声明集合s1,并添加多个元素,也可以是一个元素,也可以批量添加多个元素
# conn.sadd('s1', 'a')
# # 对于整型同样转为str, 且元素不能重复,且是无序的
# conn.sadd('s1', 'a', 'b', 'c', 1, 2, 3, 1)
# # 返回 s1 中所有元素
# print(conn.smembers('s1'))
# ------------------ zset ------------------
# # 增
# score = {
# "zhangkai": 108,
# "likai": 99,
# "wangkai": 100,
# "zhaokai": 102,
# }
# conn.zadd('score', mapping=score)
# # 查, withscores是可选参数,表示同时返回所有成员和分数
# print(conn.zrange('score', 0, -1))
# print(conn.zrange('score', 0, -1, withscores=True))
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构