【Django】Django中的缓存

缓存

1 缓存的定义

  • 定义:缓存是一类可以更快的读取数据的介质统称,也指其他可以加快数据读取的存储方式,一般用来存储临时数据,常用介质的是读取速度很快的内存。

  • 意义:视图渲染有一定成本,数据库的频繁查询过高,所以对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数,用户拿到响应的时间成本会更低。

  • Django 缓存的实现方法

    # Django 官网
    # https://docs.djangoproject.com/en/5.0/topics/cache/
    # 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

  • 缓存场景:

    1. 博客列表页

    2. 电商商品详情页

    场景特点:缓存的地方,数据变动频率较少。

2 缓存的配置

2.1 Mysql缓存

将缓存的数据存储在数据库中。

说明:尽管存储介质没有更换,但是当把一次负责查询的结果直接存储到表里,比如多个条件的过滤查询结果,可避免重复进行复杂的查询,提升效率。

settings.py 中添加 CACHES 配置块。

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.db.DatabaseCache",
        "LOCATION": "my_cache_table", # 缓存表的名称
        "TIMEOUT": 300, #缓存的保存时间,单位秒,默认值是300
        "POTIONS":{
            "MAX_ENTRIES": 300, # 缓存最大的数据条数
            "CULL_FREQUENCY": 2 # 缓存条数达到最大值时,删除 1/CULL_FREQUENCY 的缓存数据
        }
    }
}

创建缓存表。

python3 manage.py createcachetable

查看数据库。

【Django】Django中的缓存_第1张图片

使用 mysql 进行缓存,会将数据存储在指定的 mysql 表中。

【Django】Django中的缓存_第2张图片

2.2 Redis缓存

Redis是一个内存数据库,可用于缓存。

settings.py 中添加如下 CACHES 块。

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.redis.RedisCache",
        "LOCATION": "redis://username:[email protected]:6379", # username、password需要看redis是否启用身份认证,如未启用,这里则不需要配置。
        # "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
        # ],
        # 如果有多个redis服务器时,这样配置即可
    }
}

2.3 本地内存缓存

数据缓存到服务器内存中,如果配置文件中没有指定其他缓存,那么这是默认的缓存。如果你想获得内存缓存的速度优势,但又不具备运行 Memcached 的能力,可以考虑使用本地内存缓存后端。这个缓存是每进程所有(见下文)和线程安全的。

settings.py 中添加如下 CACHES 块。

# 说明:如下仅供测试,建议将数据存储到内存数据库中,如redis
# 每个进程都会有自己的私有缓存实例,这意味着不可能进行跨进程缓存,也即本地内存缓存的内存效率不是特别高,所以对于生产环境来说,它可能不是一个好的选择。对于开发来说是不错的选择。
CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
        "LOCATION": "unique_snowflake", 
    }
}

2.4 文件系统缓存

将缓存的数据存储到本地文件中。

settings.py 中添加如下 CACHES 块。

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
        "LOCATION": "/home/euansu/tmp", # 这个是文件夹的路径
        # "LOCATION": "c:\test\cache", # windows下示例
    }
}

使用文件系统缓存,会在配置的目录下生成对应的缓存文件。

【Django】Django中的缓存_第3张图片

3 整体缓存策略

整体缓存这里指的是直接缓存一个视图函数全部的结果,与下部分的局部缓存形成对照。

3.1 视图函数

from django.views.decorators.cache import cache_page
​
@cache_page(30)
def my_view(request):
    ...

代码示例

from django.shortcuts import render
from django.views.decorators.cache import cache_page
from django.http import HttpResponse
# Create your views here.
from datetime import datetime
import pytz
​
@cache_page(30)
def mysql_cache(request):
    """
    MysqlCache
    :param request: 
    :return: 
    """
    target_timezone = pytz.timezone('Asia/Shanghai')
    current_time = datetime.now()
    current_time_shanghai = current_time.replace(tzinfo=pytz.utc).astimezone(target_timezone)
​
    time_string = current_time_shanghai.strftime("%Y-%m-%d %H:%M:%S")
​
    return HttpResponse(time_string)

【Django】Django中的缓存_第4张图片

3.2 路由函数

from django.views.decorators.cache import cache_page
​
urlpatterns = [
    path('foo/', cache_page(60)(my_view)),
]

代码示例

from django.urls import path
from django.views.decorators.cache import cache_page
from . import views
​
urlpatterns = [
    path("mysql_cache/", cache_page(60)(views.mysql_cache)),
]

4 局部缓存策略

视图函数局部耗时较多,但视图函数其他区域并不耗时。

4.1 使用场景

  • 使用场景

    # 局部耗时较多,但视图函数其他区域并不耗时
    from django.shortcuts import render
    ​
    def index(request):
        # 时间复杂度极高的渲染
        book_list = Book.objects.all() # 假设此处耗时2s
        return render(request,'idnex.html',locals())

4.2 局部缓存的使用

# 先引入cache对象
​
## 方式一:使用caches['CACHE配置key']导入具体对象
from django.core.cache import cache
cache1 = caches['default']
cache2 = caches['custom']
​
## 方式二:
from django.core.cache import cache
# 相当于直接引入 CACHES 配置项中的'default'项

4.3 局部缓存的方法

  1. cache.set(key, value, timeout),存储缓存

    key:缓存的key,字符串类型
    value:Python对象
    timeout:缓存存储时间(s),默认为CACHES中的TIMEOUT值
    返回值:None
  2. cache.get(key),获取缓存

    key:缓存的key
    返回值:为key的具体值,如果没有数据,则返回None
  3. cache.add(key, value),存储缓存,只在key不存在时生效

    返回值:True[存储成功] or False[存储失败]
  4. cache.get_or_set(key, value, timeout),如果未获取到数据,则执行set操作

    返回值:value
  5. cache.set_many(dict, timeout),批量存储缓存

    dict:key和value的字典
    timout:存储的时间(s)
    返回值:插入不成功的key的数组
  6. cache.get_many(key_list),批量获取缓存数据。

    key_list:包含key的数组
    返回值:渠道的key和value的字典
  7. cache.delete(key),删除key的缓存数据。

    返回值:None
  8. cache.delete_many(key_list),批量删除。

    返回值:None

4.4 局部缓存测试

代码示例:

# 设置cache
from django.core.cache import cache
​
cache.set("uname", "euansu", 300)
​
cache.get("uname")

【Django】Django中的缓存_第5张图片

数据库中出现对应cache记录。

【Django】Django中的缓存_第6张图片

# 修改cache
cache.set("uname", "nange", 300)
​
cache.get("uname")

# 删除cache
cache.delete("uname")
​
cache.get("uname")

查询数据库,对应的缓存消失

【Django】Django中的缓存_第7张图片

5 浏览器缓存策略

浏览器也具备缓存技术,对于浏览器来说,每次向服务器发出请求都是耗时的操作,如果本身浏览器内部就具备当前 url 的内容,则一定时间内可以不必给服务器发消息,从而提升网页体验,降低服务器的请求压力。

【Django】Django中的缓存_第8张图片

浏览器缓存原理图

5.1 浏览器缓存-强缓存

不会向服务器发送请求,直接从缓存中读取资源。

服务器和浏览器之间的请求,通过如下两个响应头进行沟通。

  1. 响应头 - Expires

    定义:缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点
    样例:Expires:Thu, 02 Apr 2030 05:15:08 GMT
  2. 响应头 - Cache-Control

    在HTTP/1.1中,Cache-Control主要用于控制网页缓存,比如当 Cache-Control:max-age=120 代表请求创建后的120秒,缓存失败
    说明:目前服务器都会带着这两个头同时响应给浏览器,浏览器优先使用 Cache-Control

例如如下请求,浏览器中显示已缓存。

【Django】Django中的缓存_第9张图片

刷新页面,但浏览器中仅有一次请求。

【Django】Django中的缓存_第10张图片

查看请求响应,发现返回的响应中有 Cache-ControlExpires 两个响应头。

【Django】Django中的缓存_第11张图片

这里的 max-age=60 是通过 cache-page 实现的。

【Django】Django中的缓存_第12张图片

修改 cache_page 的参数,响应头中的 max-age 也发生变化。

【Django】Django中的缓存_第13张图片

5.2 浏览器缓存-协商缓存

图片、静态文件等这类比较费带宽且不易变化的数据,浏览器会跟服务器协商,确认当前的缓存是否可用,如果可用,服务器不需要返回数据,浏览器继续使用原来缓存的数据,如果文件不可用,则返回最新的数据。

  1. Last-Modified 响应头和 If-Modified-Since 请求头。

    说明:

    • Last-modified 为文件的最近修改时间,浏览器第一次请求静态文件时,服务器第一次请求静态文件时,服务器如果返回Last-Modified响应头,则代表该资源为需要协商的缓存。

    • 当缓存到期后,浏览器将获取到的 Last-Modified 值作为请求头 If-Modified-Since 的值,与服务器发请求协商,服务器端返回304响应码[响应体为空],代表缓存继续使用,200响应码代表缓存不可用[响应体为最新资源]。

    代码示例

    from django.http import HttpResponse
    from django.utils import timezone
    ​
    def last_modified(request):
        """
        测试last-modified请求头
        :param request:
        :return:
        """
        last_modified_time = timezone.now()
    ​
        # 检查 If-Modified-Since 头
        if request.META.get('HTTP_IF_MODIFIED_SINCE'):
            # 如果资源没有发生变化,返回 304 Not Modified
            return HttpResponse(status=304)
    ​
        # 如果资源发生了变化,设置 Last-Modified 响应头
        response = HttpResponse("Your content here")
        response['Last-Modified'] = last_modified_time.strftime('%a, %d %b %Y %H:%M:%S GMT')
        return response
    【Django】Django中的缓存_第14张图片
  2. ETag 响应头和 If-None-Match 请求头。

    说明:

    • ETag时服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,ETag就会重新生成。

    • 缓存到期后,浏览器将ETag响应头的值作为If-None-Match请求头的值,给服务器发请求协商,服务器接到请求头后,比对文件标识,不一致则认为资源不可用,返回200响应码[响应体为最新资源],可用则返回304响应码。

    代码示例

    from django.http import HttpResponse
    import hashlib
    
    def etag_func(request):
        # 获取或计算资源的内容(假设这里是一个字符串)
        resource_content = "Your content here"
    
        # 计算 ETag(使用 MD5 散列作为示例,你可以根据需要选择其他方法)
        etag = hashlib.md5(resource_content.encode('utf-8')).hexdigest()
    
        # 检查 If-None-Match 头
        if request.META.get('HTTP_IF_NONE_MATCH') == etag:
            # 如果资源没有发生变化,返回 304 Not Modified
            return HttpResponse(status=304)
    
        # 如果资源发生了变化,设置 ETag 响应头
        response = HttpResponse(resource_content)
        response['ETag'] = etag
        return response

    首次访问返回200。

    【Django】Django中的缓存_第15张图片

    再次请求,响应变为304。

你可能感兴趣的:(django,缓存,python)