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
Windows中Redis安装配置流程并实现远程访问功能
Jun 07 Redis
Redis 哨兵集群的实现
Jun 18 Redis
Redis主从配置和底层实现原理解析(实战记录)
Jun 30 Redis
Redis性能监控的实现
Jul 09 Redis
浅谈Redis位图(Bitmap)及Redis二进制中的问题
Jul 15 Redis
Redis Cluster 集群搭建你会吗
Aug 04 Redis
Redis的字符串是如何实现的
Oct 24 Redis
基于Redis zSet实现滑动窗口对短信进行防刷限流的问题
Feb 12 Redis
Redis 中使用 list,streams,pub/sub 几种方式实现消息队列的问题
Mar 16 Redis
redis调用二维码时的不断刷新排查分析
Apr 01 Redis
Redis基本数据类型Zset有序集合常用操作
Jun 01 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的栏目导航程序
2006/10/09 PHP
php SQL之where语句生成器
2009/03/24 PHP
全世界最小的php网页木马一枚 附PHP木马的防范方法
2009/10/09 PHP
两种设置php载入页面时编码的方法
2014/07/29 PHP
通过修改Laravel Auth使用salt和password进行认证用户详解
2017/08/17 PHP
php微信开发之关注事件
2018/06/14 PHP
JavaScript 盒模型 尺寸深入理解
2012/12/31 Javascript
点击显示指定元素隐藏其他同辈元素的方法
2014/02/19 Javascript
jQuery 获取、设置HTML或TEXT内容的两种方法
2014/05/23 Javascript
node.js解决获取图片真实文件类型的问题
2014/12/20 Javascript
jquery实现浮动的侧栏实例
2015/06/25 Javascript
easyui Draggable组件实现拖动效果
2015/08/19 Javascript
JavaScript重定向URL参数的两种方法小结
2016/10/19 Javascript
vue-resource + json-server模拟数据的方法
2017/11/02 Javascript
vue的三种图片引入方式代码实例
2019/11/19 Javascript
element跨分页操作选择详解
2020/06/29 Javascript
vue实现滚动鼠标滚轮切换页面
2020/12/13 Vue.js
[52:52]DOTA2上海特级锦标赛C组资格赛#1 OG VS LGD第三局
2016/02/27 DOTA
Python Mysql数据库操作 Perl操作Mysql数据库
2009/01/12 Python
python 布尔操作实现代码
2013/03/23 Python
python求众数问题实例
2014/09/26 Python
Python检测QQ在线状态的方法
2015/05/09 Python
PyTorch的Optimizer训练工具的实现
2019/08/18 Python
Python谱减法语音降噪实例
2019/12/18 Python
python ubplot使用方法解析
2020/01/10 Python
对python中各个response的使用说明
2020/03/28 Python
世界上最大的各式箱包网络零售店:eBag
2016/07/21 全球购物
微软马来西亚官方网站:Microsoft马来西亚
2019/11/22 全球购物
乐高西班牙官方商店:LEGO Shop ES
2019/12/01 全球购物
文史专业毕业生自荐信
2013/11/17 职场文书
合伙协议书范本
2014/04/21 职场文书
房地产开发项目建议书
2014/05/16 职场文书
公司应聘求职信
2014/06/21 职场文书
个人自查自纠材料
2014/10/14 职场文书
项目备案申请报告
2015/05/15 职场文书
Golang 使用Map实现去重与set的功能操作
2021/04/29 Golang