缓存

  • 缓存是用来提升服务器响应速度
  • 将执行过的操作数据 存储下来,在一定时间内,再次获取数据的时候,直接从缓存中获取
  • 缓存比较理想的方案,缓存使用内存级缓存,内存访问速度比硬盘快上万倍
  • Django内置缓存框架,并提供了几种常用的缓存

    • 基于Memcached缓存,和redis有点像,是内存级的数据库,但是支持的数据类型有点少,而且性能比redis有点低,所以这几年被redis代替。
    • 使用数据库进行缓存
    • 使用文件系统进行缓存
    • 使用本地内存进行缓存,缺点是不能多台计算机协调工作,不同电脑的内存不能互相访问
    • 提供缓存扩展接口

    缓存运行原理

    对于给定的网址,尝试从缓存中找到网址,如果页面在缓存中,直接返回缓存的页面,如果缓存中没有,一系列操作(比如查数据库)后,保存生成的页面内容到缓存系统以供下一次使用,然后返回生成的页面内容。

    缓存框架的核心目标

  • 较少的代码,不需要大幅度改动代码

    • 缓存应该尽可能的快
    • 因此围绕后端的所有框架代码应该保持在绝对最小值,特别是对于获取操作,也就是查。
  • 一致性

    • 缓存API应该时提供跨越不同缓存后端的一致接口,缓存不管是内存缓存还是数据库缓存还是用文件,我们操纵缓存时都应该一样类似于ORM。
  • 可扩展性

    • 基于开发人员的需求,缓存API应该可以在应用程序级别扩展

    缓存配置

    Django settings 中 cache 默认为

{
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
    }
}

也就是默认利用本地的内存来当缓存,速度很快。当然可能出来内存不够用的情况,其它的一些内建可用的 Backends 有

'django.core.cache.backends.db.DatabaseCache'
'django.core.cache.backends.dummy.DummyCache'
'django.core.cache.backends.filebased.FileBasedCache'
'django.core.cache.backends.locmem.LocMemCache'
'django.core.cache.backends.memcached.MemcachedCache'
'django.core.cache.backends.memcached.PyLibMCCache'

这里我们着重学习数据库缓存

1.创建缓存表

python manage.py createcachetable [table_name]

2.注册缓存表

创建完缓存表之后需要注册缓存表

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
        'TIMEOUT': 60*5,
        'OPTIONS': {
                'MAX_ENTRIES': '300',
        },
        'kEY_PREFIX': 'ROCK',
        'VERSION': '1',
    }
}

location指位置,即缓存表
timeout过期时间
version版本

这里我们可以只设置backend,location,timeout

注意,这里的backend中最后是DatabaseCache,而不是Basedatabase,否则会报错NotImplementedError

实验

新建url:

    url('^news/', views.news, name='news'),

新建views:

def news(request):
    news_list = []
    for i in range(10):
        news_list.append("最近罐装病毒开始了%d" % i)
    sleep(5)
    data = {
        'news_list':news_list
    }
    return render(request, 'news.html', context=data)

这里的sleep5是假装查询很耗时
news.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>news</title>
</head>
<body>
<ul>
    {% for news in news_list %}
    <li>{{ news }}</li>
    {% endfor %}
    
</ul>
</body>
</html>

访问网页:
加载5秒后网页

以上步骤没有使用缓存,那么我们如何使用缓存提升响应速度呢?

3.缓存使用

  • 在视图中使用(使用最多的场景)
  • 使用装饰器来实现

@cache_page() 

  - time  秒  60*50   缓存5分钟
  - cache 缓存配置,默认default
  - key_prefix前置字符串

我们在views中添加装饰器

@cache_page(60*15)# 默认保存15分钟,写成60*15是为了可读性
def news(request):
    news_list = []
    for i in range(10):
        news_list.append("最近罐装病毒开始了%d" % i)
    sleep(5)
    data = {
        'news_list':news_list
    }
    return render(request, 'news.html', context=data)

这样我们再次访问时,就不会等待5秒,而是直接从缓存中取出数据
但当15分钟过后,缓存清除,我们再次访问时,还需要等待5秒重新加载,并且存进缓存中

缓存流程图

装饰器只是做了一个缓存判断,只不过是封装好而已,接下来我们自己操作缓存

缓存底层

  • 获取缓存(存在多个缓存)
from django. core.cache import caches
cache = caches['cache_ name']
  • 获取缓存(单个)
from django . core .cache import cache
  • 缓存操作

    • cache.set

      • key
      • value
      • timeout
    • get
    • add
    • get_or_set
    • get_many
    • set_many
    • delete
    • delete_many
    • incr增加

      • incr(key,value) key对应的值添加value
    • decr减少

      • decr(key,value)key对应的值上减少value
      • 如果value不写,默认变更为1

    实验:不用装饰器来使用缓存

    之前的views修改为:

def news(request):
    # 导入的cache包是from django.core.cache import cache
    result = cache.get('news')  
    # 这里get的key为news,是让所有人共同可以访问一个统一的缓存,也可以设置成ip地址来访问对应的缓存
    if result:  # 如果result存在,即缓存存在,返回缓存中的结果
        return HttpResponse(result)

    news_list = []
    for i in range(10):
        news_list.append("最近罐装病毒开始了%d" % i)
    sleep(5)
    data = {
        'news_list': news_list
    }
    # 假如result不存在,我们将数据存到缓存中,再返回到网页
    response = render(request, 'news.html', context=data)  # 渲染数据
    cache.set('news', response.content, timeout=60)  # 存入缓存
    return response

使用Redis做缓存

刚刚我们学习的是使用数据库来作为缓存,这种做法还是需要到硬盘中去查找,而目前性能问题最大的就是硬盘速度,接下来我们学习使用内存级缓存
然而Django中自带没有Redis缓存,是有大佬自己研发的,不是原生的,所以需要我们去安装

Redis缓存安装

pip install django-redis
pip install django-redis-cache

settings切换成Redis缓存配置

CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',

        }
    }
}

LOCATION指的是缓存存储位置,实验在本机上所以只需要写自己的电脑就可以了
CLIENT_CLASS是客户端的类,作用是创建了一个单例,我们在使用redis时候全局只有一个客户端窗口就可以了,因为多个客户端对于我们使用缓存没有性能上的提升,并且数据库的服务端连接客户端数量也是有上限的。(mysql默认连接客户端为100超过的就连接不上)

接着我们要在本地安装redis,安装过程略,接着我们启动服务端

C:\redis>redis-server.exe redis.windows.conf

然后观察网页,成功显示

接着我们进入redis数据库进行查看

C:\redis>redis-cli          启动客户端
127.0.0.1:6379> select 1     选择库
OK

127.0.0.1:6379[1]> keys *         查看有哪些表
1) ":1:news"                    
127.0.0.1:6379[1]> get :1:news        查看缓存表
"\x80\x04\x95g\x02\x00\x00\x00\x00\x00\x00B`\x02\x00\x00<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>news</title>\n</head>\n<body>\n<ul>\n    \n    <li>\xe6\x9c\x80\xe8\xbf\x91\xe7\xbd\x90\xe8\xa3\x85\xe7\x97\x85\xe6\xaf\x92\xe5\xbc\x80\xe5\xa7\x8b\xe4\xba\x860</li>\n    \n    <li>\xe6\x9c\x80\xe8\xbf\x91\xe7\xbd\x90\xe8\xa3\x85\xe7\x97\x85\xe6\xaf\x92\xe5\xbc\x80\xe5\xa7\x8b\xe4\xba\x861</li>\n    \n    <li>\xe6\x9c\x80\xe8\xbf\x91\xe7\xbd\x90\xe8\xa3\x85\xe7\x97\x85\xe6\xaf\x92\xe5\xbc\x80\xe5\xa7\x8b\xe4\xba\x862</li>\n    \n    <li>\xe6\x9c\x80\xe8\xbf\x91\xe7\xbd\x90\xe8\xa3\x85\xe7\x97\x85\xe6\xaf\x92\xe5\xbc\x80\xe5\xa7\x8b\xe4\xba\x863</li>\n    \n    <li>\xe6\x9c\x80\xe8\xbf\x91\xe7\xbd\x90\xe8\xa3\x85\xe7\x97\x85\xe6\xaf\x92\xe5\xbc\x80\xe5\xa7\x8b\xe4\xba\x864</li>\n    \n    <li>\xe6\x9c\x80\xe8\xbf\x91\xe7\xbd\x90\xe8\xa3\x85\xe7\x97\x85\xe6\xaf\x92\xe5\xbc\x80\xe5\xa7\x8b\xe4\xba\x865</li>\n    \n    <li>\xe6\x9c\x80\xe8\xbf\x91\xe7\xbd\x90\xe8\xa3\x85\xe7\x97\x85\xe6\xaf\x92\xe5\xbc\x80\xe5\xa7\x8b\xe4\xba\x866</li>\n    \n    <li>\xe6\x9c\x80\xe8\xbf\x91\xe7\xbd\x90\xe8\xa3\x85\xe7\x97\x85\xe6\xaf\x92\xe5\xbc\x80\xe5\xa7\x8b\xe4\xba\x867</li>\n    \n    <li>\xe6\x9c\x80\xe8\xbf\x91\xe7\xbd\x90\xe8\xa3\x85\xe7\x97\x85\xe6\xaf\x92\xe5\xbc\x80\xe5\xa7\x8b\xe4\xba\x868</li>\n    \n    <li>\xe6\x9c\x80\xe8\xbf\x91\xe7\xbd\x90\xe8\xa3\x85\xe7\x97\x85\xe6\xaf\x92\xe5\xbc\x80\xe5\xa7\x8b\xe4\xba\x869</li>\n    \n\n</ul>\n</body>\n</html>\x94."
127.0.0.1:6379[1]> ttl :1:news           查看过期时间
(integer) -2                             -2代表缓存已过期,每过期的时候显示秒数倒计时


多缓存

Django还支持多缓存,可以将缓存一部分存放在数据库缓存,一部分存放在内存中
我们添加两个缓存

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
        'TIMEOUT': 60 * 5,
    },
    'redis_backend': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': 'redis://127.0.0.1:6379/1',
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',

        }
    }
}

那么我们怎么指定哪些数据放在哪个缓存中呢?

方法一:不用装饰器时指定缓存存放


方法二:装饰器指定缓存位置

新建urls:

url('^jokes/', views.jokes, name='jokes'),

切换至数据库缓存(default)

views:

@cache_page(60, cache='default')
def jokes(request):
    sleep(5)
    return HttpResponse('jokeList')

这里装饰器第一个参数60是设置缓存过期时间
第二个参数设置缓存保存的库的名字

接着我们访问jokes,使得加载缓存入default(也就是数据库缓存)

为了验证缓存真的按照我们的要求存放,我们打开缓存表

可以找到这一行的记录,我们将value的值进行base64的解码


解码出来可以看到,一部分是乱码,因为这里不光用了base64编码,也用了其他的编码,不过我们仍然可以从中得出内容就是刚刚存放的缓存。

切换至redis缓存(redis_backend)

views改为:

@cache_page(60, cache='redis_backend')
def jokes(request):
    sleep(5)
    return HttpResponse('jokeList')

接着我们访问网页,使缓存存放,然后访问redis

可以看到,多出来一条记录,而且过期时间在1分钟以内,说明就是我们刚刚存放的缓存
并且我们可以使用get 来获取内容

虽然内容是十六进制,但是经过加密,我们不能轻易解码出来内容,redis为我们做了安全处理。
至此缓存结束。

在以后我们只需要改动redis的IP,这台主机可以只存放redis,让所有人都来访问这个主机来获取缓存。

60s内访问30次以上:

所以我们的爬虫不能直接把人家网站怼瘫痪了,这样我们就爬取不了很多数据,而且服务器的运维人员会对爬虫做限制策略,所以我们编写爬虫时需要控制速度,让爬取的服务器正常运转。

AOP只要是介于请求和响应之间的事情我们都能做

最后修改:2024 年 03 月 13 日
如果觉得我的文章对你有用,请随意赞赏