Django使用redis配置缓存的方法


Posted in Redis onJune 01, 2021

对于非经常更新的服务器数据,若每次都从硬盘读取一次,会浪费服务器资源、拖慢响应速度,而且数据更新频率较高,服务器负担比较大。若保存到数据库,还需要额外建立一张对应的表存储数据。一个更好的方法是在Django中使用Redis进行缓存,下面通过本文给大家介绍Django使用redis配置缓存的方法。

前言

  动态网站的基本权衡是,它们是动态的。每次用户请求页面时,Web服务器都会进行各种计算 - 从数据库查询到模板呈现再到业务逻辑 - 以创建站点访问者看到的页面。从处理开销的角度来看,这比标准的文件读取文件系统服务器要耗时多了。对于大多数Web应用程序来说,这种开销并不是什么大问题。因为大多数Web应用程序只是中小型网站,没有拥有一流的流量。但对于中到高流量的站点,尽可能减少开销是至关重要的,这就是缓存的用武之地。缓存某些内容是为了保存昂贵计算的结果,这样就不必在下次执行计算。
  Django框架带有一个强大的缓存系统,可以保存动态页面,因此不必为每个请求计算它们。Django提供不同级别的缓存粒度:可以缓存特定视图的输出,也可以只缓存页面中难以生成的部分或者可以缓存整个站点。
  Redis是一个内存数据库(现在已经支持内存数据持久化到硬盘当中,重新启动时,会自动从硬盘进行加载),由于其性能极高,因此经常作为中间件、缓存使用。

django应用redis缓存

django中安装第三方库,使用如下命令

pip3 install django-redis

1.settings配置

首先,我们在settings.py中配置如下代码

CACHES = {
    # default 是缓存名,可以配置多个缓存
    "default": {
        # 应用 django-redis 库的 RedisCache 缓存类
        "BACKEND": "django_redis.cache.RedisCache",
        # 配置正确的 ip和port
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            # redis客户端类
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            # redis连接池的关键字参数
            "CONNECTION_POOL_KWARGS": {
                "max_connections": 100
            }
            # 如果 redis 设置了密码,那么这里需要设置对应的密码,如果redis没有设置密码,那么这里也不设置
            # "PASSWORD": "123456",
        }
    }
}

2.全站缓存

2.1 全站缓存的2个中间件

  • FetchFromCacheMiddleware :从缓存中读取数据

缓存状态为200的GETHEAD请求的响应(除非响应头中设置不进行缓存)

对具有不同查询参数的相同URL的请求的响应被认为是各自不同的页面,并且被分别单独缓存。

该中间件会使用与对应的GET请求相同的响应头来回答HEAD请求,即可以为HEAD请求返回缓存的GET响应。

  • UpdateCacheMiddleware :将数据更新到缓存中

该中间件会自动在每个响应中设置几个headers

设置Expires为当前日期/时间 加上 定义的CACHE_MIDDLEWARE_SECONDS值,GMT时间

设置响应的Cache-Controlmax-age,值是定义的CACHE_MIDDLEWARE_SECONDS值。

  • 如果视图设置了自己的缓存时间(即设置了Cache-Control max age),那么页面将被缓存直到到期时间,而不是CACHE_MIDDLEWARE_SECONDS
  • 如果USE_I18N设置为True,则生成的缓存key将包含当前语言的名称,这样可以轻松缓存多语言网站,而无需自己创建缓存密钥。
  • 如果 USE_L10N设置为True 并且 USE_TZ被设置为True,缓存key也会包括当前语言

settings的中间件中设置:

MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',
    # 其他中间件...
    'django.middleware.cache.FetchFromCacheMiddleware',
]

注意:UpdateCacheMiddleware必须是第一个中间件,FetchFromCacheMiddleware必须是最后一个中间件

2.2 全站缓存的必填设置

将以下必须设置添加到Djangosettings文件中

CACHE_MIDDLEWARE_ALIAS = 'default'
CACHE_MIDDLEWARE_SECONDS = 60*60
CACHE_MIDDLEWARE_KEY_PREFIX = "cache_redis_demo_first"

配置解释如下:

  • CACHE_MIDDLEWARE_ALIAS:用于存储的缓存别名
  • CACHE_MIDDLEWARE_SECONDS:每个页面应缓存的秒数
  • CACHE_MIDDLEWARE_KEY_PREFIX:用于生成缓存key的前缀,如果使用相同的Django安装在多个站点之间共享缓存,请将其设置为站点名称或此Django实例特有的其他字符串,以防止发生密钥冲突。如果你不在乎,请使用空字符串。

2.3 全站缓存示例

接着我们在视图中写入如下函数:

def index(request):
    # 通过设置时间戳,进行多次访问,可以看到时间戳的变化,就可以得知是否是缓存页面了
    return HttpResponse('当前时间戳:' + str(time.time()))

我们打开浏览器访问127.0.0.1/redis/,多次访问该url,发现时间戳不会改变,这是因为我们在配置中设置了缓存时间为1个小时。
我们可以打开浏览器的网络请求中查看response header,查看max_ageExpires,如下图

Django使用redis配置缓存的方法

我们会发现响应头中已经有了缓存的时间,说明我们缓存配置成功了

3.视图函数缓存

  一般情况下,我们不会使用全局缓存,因为全局缓存,只要服务器返回状态码是200,他都会将其缓存下来,这样会影响性能,所以我们一般都会使用视图缓存,针对某个视图,需要进行缓存,则使用缓存。

3.1通过装饰器cache_page

 

from django.views.decorators.cache import never_cache, cache_page
@cache_page(20)
def view_cache(request, num):
    return HttpResponse(f"num:{num},时间戳:{time.time()}")

cache_page除了默认的timeout参数外,还有两个可选的关键字参数

cache,示例代码:@cache_page(60 * 15, cache="special_cache"), 该cache指向settings中配置的缓存的名称,默认是"default"

key_prefix:缓存key的前缀,与CACHE_MIDDLEWARE_KEY_PREFIX功能相同

如果多个url指向同一个视图函数,会为每个url建立一个单独的缓存,例如:

urlpatterns = [
    path('view_cache/<int:num>/', views.view_cache, name="view_cache")
]

/view_cache/1//view_cache/2/请求会分别进行缓存

3.2通过urls中配置cache_page

URLconf中指定视图缓存,而不是在视图函数上硬编码装饰器,可以进一步解耦缓存和视图函数之间的关系,使用起来更灵活

from django.views.decorators.cache import cache_page
 
urlpatterns = [
    path('view_cache/<int:num>/', cache_page(20)(views.view_cache), name="view_cache")
]

以上2种方式作用是一样的,这里我们更加推荐3.2这种写法

4.低级缓存

  有时我们不想缓存整个页面数据,而只是想缓存某些费时查询并且基本不会改变的数据,可以通过一个简单的低级缓存API实现,该API可以缓存任何可以安全picklePython对象:字符串,字典,模型对象列表等

django.core.cache.caches

from django.core.cache import caches
cache1 = caches['myalias']
cache2 = caches['myalias']
# 判断为True
if cache1 is cache2: 
    ...

说明:

  • 可以通过CACHES类似字典一样的方式访问settings中配置的缓存,在同一个线程中重复请求相同的别名将返回相同的对象
  • 如果指定的myalias不存在,将引发 InvalidCacheBackendError
  • 为了线程安全性,为会每个线程返回缓存的不同实例
  • 作为快捷方式, 默认缓存(default)可以使用 django.core.cache.cache
# 使用 default 缓存
from django.core.cache import cache

# 上面的cache等同于下面的写法
from django.core.cache import caches
cache = caches['default']

django.core.cache.cache

from django.core.cache import cache

# 使用 redis 的一般用法
cache.set('manul_set', 'ok')
manul_set = cache.get('manul_set')

# 可以手动设置 timeout,如果不指定timeout,默认是 300秒,如果指定为None,则代表没有过期时间
cache.set("key", "value", timeout=None)

# 可以获取key的超时设置(ttl:time to live)
# 返回值的3种情况:
# 0: key 不存在 (或已过期)
# None: key 存在但没有设置过期
# ttl: 任何有超时设置的 key 的超时值
cache.set("foo", "value", timeout=25)
cache.ttl("foo") # 得到 25 
cache.ttl("not-existent") # 得到 0

# 让一个值永久存在
cache.persist("foo")
cache.ttl("foo") # 得到 None

# 指定一个新的过期时间
cache.set("foo", "bar", timeout=22)
cache.ttl("foo") # 得到 22
cache.expire("foo", timeout=5)
cache.ttl("foo") # 得到 5

# 支持 redis 分布式锁, 使用 上下文管理器 分配锁
with cache.lock("somekey"):
    do_some_thing()
    
# 使用全局通配符的方式来检索或者删除键
cache.keys("foo_*")  # 返回所有匹配的值, 如 ["foo_1", "foo_2"]


# 删除 键
cache.delete_pattern("foo_*")  # 支持通配符

实战案例

首先创建个common文件夹,然后在文件夹下面创建cache_helper.py文件,写入如下代码

from django.core.cache import cache


def get_cache_or_exc_func(key, func, *args, **kwargs):
    """
    根据传入的key和func,先获取缓存内容,没有则使用func计算并保存结果
    :param key: 缓存的key
    :param func: 计算函数
    :param args: 可变参数
    :param kwargs: 可变字典
    :return: 缓存的n内容或func计算的结果
    """
    # 加上cache锁
    with cache.lock(key+'lock'):
        # 获取缓存中的变量
        result = cache.get(key)
        if result:
            # 存在,则直接返回缓存结果
            return result
        else:
            # 不存在,则计算数据,得到结果
            result = func(*args, **kwargs)
            # 将结果保存到缓存中
            cache.set(key, result)
            # 返回结果
            return result

然后配置url路径,如下

urlpatterns = [
    path('lower_level_cache/', views.lower_level_cache, name="lower_level_cache"),
]

最后在视图中,写入2个函数

def get_result():
    """做一些费时但不经常变更的操作,这里模拟等待3秒"""
    time.sleep(3)
    return 'ok'


def lower_level_cache(request):
    result = get_cache_or_exc_func('test_key', get_result)
    return JsonResponse({"result": result})

现在我们打开浏览器,访问127.0.0.1/redis/low_level_cache/,我们会发现,浏览器不会马上响应,而是等待了3秒,因为我们代码中模拟等待了3秒,而且我们是第一次访问,没有缓存,当第二次访问时,就立马响应了,原因是此时已经有了缓存

5.session缓存

settings.py文件中,配置如下代码即可

# 配置session的引擎为cache
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
# 此处别名依赖缓存的设置
SESSION_CACHE_ALIAS = 'default'

以上就是Django使用redis配置缓存的详细内容,更多关于redis配置缓存的资料请关注三水点靠木其它相关文章!

Redis 相关文章推荐
Redis持久化与主从复制的实践
Apr 27 Redis
Redis高级数据类型Hyperloglog、Bitmap的使用
May 24 Redis
SpringBoot 集成Redis 过程
Jun 02 Redis
Redis做数据持久化的解决方案及底层原理
Jul 15 Redis
Redis中一个String类型引发的惨案
Jul 25 Redis
浅谈Redis的keys命令到底有多慢
Oct 05 Redis
redis击穿 雪崩 穿透超详细解决方案梳理
Mar 17 Redis
sentinel支持的redis高可用集群配置详解
Apr 01 Redis
Redis超详细讲解高可用主从复制基础与哨兵模式方案
Apr 07 Redis
Redis中key的过期删除策略和内存淘汰机制
Apr 12 Redis
Redis特殊数据类型HyperLogLog基数统计算法讲解
Jun 01 Redis
Redis主从复制操作和配置详情
Sep 23 Redis
详解Redis集群搭建的三种方式
May 31 #Redis
浅谈Redis主从复制以及主从复制原理
5分钟教你docker安装启动redis全教程(全新方式)
May 29 #Redis
详解缓存穿透击穿雪崩解决方案
浅谈Redis的几个过期策略
May 27 #Redis
Redis Cluster 字段模糊匹配及删除
May 27 #Redis
redis哨兵常用命令和监控示例详解
May 27 #Redis
You might like
php相对当前文件include其它文件的方法
2015/03/13 PHP
深入理解PHP的远程多会话调试
2017/09/21 PHP
PHP通过文件保存和更新信息的方法分析
2019/09/12 PHP
php使用event扩展的io复用测试的示例
2020/10/20 PHP
让AJAX不依赖后端接口实现方案
2012/12/03 Javascript
js中escape对应的C#解码函数 UrlDecode
2012/12/16 Javascript
jQuery的Scrollify插件实现滑动到页面下一节点
2015/07/05 Javascript
js如何打印object对象
2015/10/16 Javascript
Node.js巧妙实现Web应用代码热更新
2015/10/22 Javascript
Jq通过td获取同行其它列td的方法
2016/10/05 Javascript
Node.js读取文件内容示例
2017/03/07 Javascript
nodejs入门教程五:连接数据库的方法分析
2017/04/24 NodeJs
jQuery选取所有复选框被选中的值并用Ajax异步提交数据的实例
2017/08/04 jQuery
jQuery实现左右滑动的toggle方法
2018/03/03 jQuery
浅谈JS for循环中使用break和continue的区别
2020/07/21 Javascript
Vue 数据绑定的原理分析
2020/11/16 Javascript
Python实现压缩与解压gzip大文件的方法
2016/09/18 Python
基于Python Numpy的数组array和矩阵matrix详解
2018/04/04 Python
Python 找到列表中满足某些条件的元素方法
2018/06/26 Python
Python实现堡垒机模式下远程命令执行操作示例
2019/05/09 Python
Flask框架钩子函数功能与用法分析
2019/08/02 Python
python文件读写代码实例
2019/10/21 Python
浅谈python输出列表元素的所有排列形式
2020/02/26 Python
通过cmd进入python的步骤
2020/06/16 Python
使用python实现下载我们想听的歌曲,速度超快
2020/07/09 Python
浅谈css3中calc在less编译时被计算的解决办法
2017/12/04 HTML / CSS
阿根廷旅游网站:almundo阿根廷
2018/02/12 全球购物
中兴通讯全球官方网站:ZTE
2020/12/26 全球购物
班组长竞聘书
2014/03/31 职场文书
卫生系统先进事迹
2014/05/13 职场文书
教师考核材料
2014/05/21 职场文书
管理标语大全
2014/06/24 职场文书
幼儿园教师个人总结
2015/02/05 职场文书
关于成立领导小组的通知
2015/04/23 职场文书
六年级数学教学反思
2016/02/16 职场文书
简述python四种分词工具,盘点哪个更好用?
2021/04/13 Python