简介
为什么要用缓存?
我们知道,在Django中,请求到达视图后,视图会从数据库取数据放到模板中进行动态渲染,渲染后的结果就是用户看到的html页面。但是,如果每次请求都从数据库取数据并渲染,将极大降低性能,不仅服务器压力大,而且客户端也无法即时获得响应。如果能将渲染后的结果放到速度更快的缓存中,每次有请求过来,先检查缓存中是否有对应的资源,如果有,直接从缓存中取出来返回响应,节省取数据和渲染的时间,不仅能大大提高系统性能,还能提高用户体验。
另外,缓存只是一类统称,一般其介质是速度很快的内存,但也可以是能加快数据读取的其它方式。
什么时候适合用缓存?
对页面实时性要求不高的页面,可以用缓存。比如博客文章,假设用户一天更新一篇文章,那么可以为博客设置1天的缓存;一天后会刷新。另外像购物网站,商品的描述信息几乎不会变化,而商品的库存数量却要求根据用户的购买实时更新,因此也应该应用合适的缓存策略。
缓存方式
Django中提供了6中缓存方式,如果要使用缓存,需要现在settings.py中进行配置,然后再应用。
配置
开发调试
开发调试实际不做缓存,这里不做赘述。
内存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
'TIMEOUT': 300,
# 缓存超时时间(默认300,None表示永不过期,0表示立即过期)
'OPTIONS':{
'MAX_ENTRIES': 300,
# 最大缓存个数(默认300)
'CULL_FREQUENCY': 3,
# 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3);实际中一般不这么用,太简单粗暴了。
}
}
}
以上的'TIMEOUT'、'OPTIONS'
项在下面的几种缓存配置中是通用的。
另外,我们也可以在setting.py中单独配置CACHE_MIDDLEWARE_SECONDS = 10
缓存超时时间,其优先级高于CACHES
中的TIMEOUT
。
内存缓存是当前机器的内存,不常用。
文件
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': 'D:\cache',
}
}
文件即硬盘缓存,小网站的低成本解决方案。
tips: 数据库连接是socket通信,速度不如本地文件快;而且数据库拿来的数据还要渲染,而文件中已经是渲染好的字符串,因此比起数据库还是要快。
数据库
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table', # 数据库表
}
# 执行创建表命令 python manage.py createcachetable
}
数据库缓存,存储的是渲染好的字符串,少了渲染这一步,稍微快一点。不常用。
Memcache缓存(python-memcached模块)
# 此缓存使用python-memcached模块连接memcache
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': 'unix:/tmp/memcached.sock',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
# 我们也可以给缓存机器加权重,权重高的承担更多的请求,如下
'LOCATION': [
('172.19.26.240:11211',5),
('172.19.26.242:11211',1),
]
}
}
用一台具有超大内存的机器,安装Memcache或Redis软件,提供ip和端口,本机向其发送指令来存取数据,而且可以支持分布式存储。但是内存数据断电丢失,因此一般配置两台机器,各存一半的数据,即使挂了一台,还有一台分担数据库压力。一旦缓存都挂了,如果有大量请求直接到达数据库,可能会导致程序崩掉。
Memcache缓存(pylibmc模块)
用起来用上一种是一样的,只是实现用的模块不一样。配置时,将’BACKEND’: 的值改为'django.core.cache.backends.memcached.PyLibMCCache'
就好了
应用
全站缓存:粒度大
全站缓存可以在中间件的最后一个来做,只有验证合法的请求,才允许返回响应。如果请求的资源有缓存,返回缓存作为响应体,通过中间件时再加上响应头返回给客户端。因为中间件可以对响应进行处理,甚至修改返回值,因此需要在出去时的最后一个中间件(即进来的时走的第一个中间件)给缓存。
但是Django中已经提供好了这两个中间件,我们直接加到setting.py中就可以了。
MIDDLEWARE = [
'django.middleware.cache.UpdateCacheMiddleware',
# 其他中间件...
'django.middleware.cache.FetchFromCacheMiddleware',
]
请求进来时,通过FetchFromCacheMiddleware
获取资源,响应出去时,如果缓存中不存在请求的资源,则通过django.middleware.cache.UpdateCacheMiddleware
将响应字符串保存在缓存中。
全站缓存适合实时性要求不高的网站,比如个人博客。
单独视图函数:粒度适中
这里,我们通过文件缓存的配置方式来实践缓存。
方式一:在视图上方加装饰器@cache_page(seconds)
from django.shortcuts import render
from time import ctime
from django.views.decorators.cache import cache_page
@cache_page(5) # 这里设置5秒缓存超时
def test(request):
"""这里通过获取当前时间来模拟从数据库中取数据"""
now = ctime()
return render(request, 'test.html', {'now': now})
<!-- test.html -->
<body>
<h1>当前时间是:</h1>
<h3>{{ now }}</h3>
</body>
这样我们通过浏览器访问当前视图时,会获取到当前时间,但是当我们刷新页面,时间并不会变化,除非5秒后再刷新:
检查本地磁盘,发现生成了缓存文件:
方式二:在路由中进行配置
from django.views.decorators.cache import cache_page
urlpatterns = [
url(r'^test.html/$', cache_page(5)(views.test)),
# 将cache_page(5) 加到视图前面
]
局部模板:粒度小,更灵活
通过在模板中应用{% cache %}
来进行局部缓存控制,还是上面的栗子,我们将模板简单改一下:
<!-- 引入TemplateTag -->
{% load cache %}
<body>
<h1>当前时间是:</h1>
<h3>{{ now }}</h3>
<h1>当前时间是(使用缓存):</h1>
{% cache 5 key %}
<h3>{{ now }}</h3>
{% endcache %}
<!-- 注意 cache 要求两个参数:seconds, key -->
</body>
重新访问页面,3秒后刷新以下,发现使用缓存的时间没有变化