缓存
- 缓存是用来提升服务器响应速度
- 将执行过的操作数据 存储下来,在一定时间内,再次获取数据的时候,直接从缓存中获取
- 缓存比较理想的方案,缓存使用内存级缓存,内存访问速度比硬盘快上万倍
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.缓存使用
- 在视图中使用(使用最多的场景)
- 使用装饰器来实现
- 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只要是介于请求和响应之间的事情我们都能做
此处评论已关闭