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 相关文章推荐
Redis5之后版本的高可用集群搭建的实现
Apr 27 Redis
Redis IP地址的绑定的实现
May 08 Redis
redis cluster支持pipeline的实现思路
Jun 23 Redis
缓存替换策略及应用(以Redis、InnoDB为例)
Jul 25 Redis
Redis 常见使用场景
Aug 30 Redis
使用redis实现延迟通知功能(Redis过期键通知)
Sep 04 Redis
linux下安装redis图文详细步骤
Dec 04 Redis
解决Redis启动警告问题
Feb 24 Redis
Redis基本数据类型Set常用操作命令
Jun 01 Redis
浅谈Redis缓冲区机制
Jun 05 Redis
基于redis+lua进行限流的方法
Jul 23 Redis
Redis配置外网可访问(redis远程连接不上)的方法
Dec 24 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 cout&amp;lt;&amp;lt;的一点看法
2010/01/24 PHP
php实现查看邮件是否已被阅读的方法
2013/12/03 PHP
javascript读写XML实现广告轮换(兼容IE、FF)
2013/08/09 Javascript
JavaScript动态创建div属性和样式示例代码
2013/10/09 Javascript
使用JQ来编写最基本的淡入淡出效果附演示动画
2014/10/31 Javascript
Javascript基础教程之for循环
2015/01/18 Javascript
简述JavaScript中正则表达式的使用方法
2015/06/15 Javascript
jQuery Validate初步体验(一)
2015/12/12 Javascript
js replace(a,b)之替换字符串中所有指定字符的方法
2016/08/17 Javascript
angularJs的ng-class切换class
2017/06/23 Javascript
select2 ajax 设置默认值,初始值的方法
2018/08/09 Javascript
Python中if __name__ == &quot;__main__&quot;详细解释
2014/10/21 Python
python从入门到精通(DAY 1)
2015/12/20 Python
Python中functools模块的常用函数解析
2016/06/30 Python
基于Python列表解析(列表推导式)
2018/06/23 Python
Django框架使用富文本编辑器Uedit的方法分析
2018/07/31 Python
python itchat给指定联系人发消息的方法
2019/06/11 Python
python算法与数据结构之单链表的实现代码
2019/06/27 Python
django将数组传递给前台模板的方法
2019/08/06 Python
浅谈keras中的Merge层(实现层的相加、相减、相乘实例)
2020/05/23 Python
python能开发游戏吗
2020/06/11 Python
Python实现区域填充的示例代码
2021/02/03 Python
英国手工布艺沙发在线购买:Sofas & Stuff
2018/03/02 全球购物
We Fashion荷兰:一家国际时装公司
2018/04/18 全球购物
三星印度官网:Samsung印度
2019/08/03 全球购物
专科毕业生就业推荐信
2013/11/01 职场文书
园林施工员岗位职责
2013/12/11 职场文书
普通员工辞职信
2014/01/17 职场文书
《童趣》教学反思
2014/02/19 职场文书
美丽乡村建设实施方案
2014/03/23 职场文书
公司合作意向书
2014/04/01 职场文书
小学网上祭英烈活动总结
2014/07/05 职场文书
2015庆祝七一建党节94周年活动总结
2015/03/20 职场文书
2015教师年度考核评语
2015/03/25 职场文书
物业管理交接协议书
2016/03/24 职场文书
聊聊配置 Nginx 访问与错误日志的问题
2022/05/25 Servers