缓存Cache
Django 缓存系统
Django是动态网站,每次用户请求页面时,Web服务器都会进行各种计算:从数据库查询到模板呈现再到业务逻辑 ,使得访问者能看到创建的站点的内容。 从处理开销的角度来看,这比从标准的文件服务器读取文本要大得多。对于大多数Web应用程序来说,这种开销并不是什么大问题。 大多数Web应用程序,只是一些中小型网站,访问流量不大。 但对于中高流量站点,必须尽可能减少开销。而且Django还支持共享缓存。
可以通过伪代码了解缓存机制在动态网站是如何进行的。
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
对于给定的网址,尝试从缓存中找到网址,如果页面在缓存中,直接返回缓存的页面,如果缓存中没有,一系列操作(比如查数据库)后,保存生成的页面内容到缓存系统以供下一次使用,然后返回生成的页面内容。
Django带有一个强大的缓存系统,可以保存动态页面,这样就不必每次请求时都进行计算。 为方便起见,Django提供不同级别的缓存:可以缓存特定视图的输出,也可以只缓存难以生成的部分,或者缓存整个站点。
Django也适用于“下游”(downstream
)缓存,例如Squid和基于浏览器的缓存。 这些不是直接控制的缓存类型,但是可以提供有关缓存网站部分的提示(通过HTTP标头)以及如何缓存。
CACHES
要在Django使用缓存,需要在setting
中设置CACHES
并指定BACKEND
缓存后端,Django自身缓存后台支持几种机制,
- Local-memory caching:Django默认使用的缓存系统,数据存储在本地内存中:
- Dummy caching (for development):开发时使用,只有接口;
- Filesystem caching:文件缓存,使用文件来缓存数据;
- Database caching:数据库缓存,使用项目数据库的数据表缓存数据,注意使用了多数据库时的路由设置;
- Memcached:使用Memcached作为缓存;
除了这些之外还可以使用自定义的缓存后端,如使用Redis作为缓存后端
CACHES = {
# 默认缓存
'default':{
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
},
# 使用文件缓存
'filecached': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
},
# 使用memcached做缓存
'memcached': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': ['127.0.0.1:11211',],
'TIMEOUT': 60 * 60,
},
# 开发时使用的虚拟缓存,只是实现接口,实际不缓存
'dummycached':{
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
},
# 使用数据库缓存,使用项目的数据库中的表做缓存
'dbcached': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
},
}
可以在缓存控制后端添加一些额外的参数:
TIMEOUT
:设置默认的缓存超时时间(秒),这个时间默认是300秒,当为None
时代表缓存不会过期,为0
时代表不使用缓存。KEY_PREFIX
:如果要在服务器之间或生产环境与开发环境之间共享缓存实例,则一台服务器缓存的数据可能会被另一台服务器使用。 如果服务器之间的缓存数据格式不同,这可能会导致一些非常难以诊断的问题。为了防止这种情况,Django提供了为服务器使用的所有缓存键添加前缀的功能。 当保存或检索特定的缓存键时,Django将自动为缓存键添加KEY_PREFIX缓存设置的值。VERSION
:Django服务器生成的缓存键的默认版本号。如果程序代码更改后需要清除原有缓存,可以通过VERSION指定缓存的版本号和KEY_PREFIX结合指定新的缓存值。KEY_FUNCTION
:指定生成缓存秘钥的函数,需要将key
,prefix
,version
结合起来 ,得到最终的缓存秘钥OPTION
:传递给缓存后端的选项,使用第三方支持的缓存后端时,会将这些选项传输给它们。
MAX_ENTRIES
:删除旧值之前缓存中允许的最大条目数。此参数默认为300。
CULL_FREQUENCY
:达到MAX_ENTRIES时剔除的条目部分。实际比率为1 / CULL_FREQUENCY,因此将CULL_FREQUENCY设置为2可在达到MAX_ENTRIES时剔除一半条目。此参数应为整数,默认为3。
CULL_FREQUENCY
的值为0意味着在达到MAX_ENTRIES时将转储整个缓存。在一些后端(特别是数据库)上,这使得剔除速度更快,但代价是更多的缓存未命中。
使用缓存
缓存整个网站
使用缓存最简单方法就是设置缓存整个网站,需要在MIDDLEWARE
内最前面
添加django.middleware.cache.UpdateCacheMiddleware
,在最后面
添加django.middleware.cache.FetchFromCacheMiddleware
。
在settings
中添加以下值:
CACHE_MIDDLEWARE_ALIAS
:存储用的缓存别名(CACHES
中设置的值)CACHE_MIDDLEWARE_SECONDS
:页面被缓存的时间CACHE_MIDDLEWARE_KEY_PREFIX
:当缓存被不同的站点使用时,用来防止缓存key值冲突的,一般设为站点名字。
FetchFromCacheMiddleware中间件用来缓存通过GET和HEAD方法获取的状态码为200的响应。同一个url,带有不同的查询字符串,会当做不同的页面分别缓存。
UpdateCacheMiddleware中间件在响应HttpResponse中设置几个headers:
设置Last-Modified为页面最新的刷新时间,设置Expires为过期时间(现在时间加CACHE_MIDDLEWARE_SECONDS),设置Cache-Control页面最大有效期(CACHE_MIDDLEWARE_SECONDS)。
views逻辑函数也可以自己设置过期时间:
使用django.views.decorators.cache.cache_control()设置缓存过期时间,使用django.views.decorators.cache.never_cache()禁止缓存。
页面缓存
使用一个装饰器@cache_page
装饰视图函数来缓存某个页面:
from django.views.decorators.cache import cache_page
@cache_page(60 * 15, cache='memcached')
def index(request):
context = {'name':'<b>Hello</b>'}
return render(request, 'index.html', context)
cache_page的第一个参数指定缓存有效时间(秒),cache
参数指定使用的是CACHES
中的哪一种缓存,key_prefix
参数与CACHE_MIDDLEWARE_KEY_PREFIX相同。
除了使用装饰器,也可以在URLconf
中就指定使用缓存:
from django.views.decorators.cache import cache_page
urlpatterns = [
path('foo/<int:code>/', cache_page(60 * 15)(my_view)),
]
在模板中使用缓存
{% cache %}
模板标签会缓存block内容,至少包括两个参数:缓存时间和缓存片段的name。
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
还可以根据变化的动态数据为一个片段缓存不同的拷贝:
{% load cache %}
{% cache 500 sidebar request.user.username %}
.. sidebar for logged in user ..
{% endcache %}
Cache API
有时候缓存整个网站或是缓存整个页面都不会有太大好处,反而带来负担。例如,某个视图取决于几个开销很大的查询,其结果会在不同的时间会发生变化。这种情况不适用缓存整个站点或是网页,但仍然想要缓存那些很少改变的数据。这就需要Django提供的low-levelcache API
,这样就可以缓存任意能被安全存储的Python对象:字符串、字典表、模型对象等。
获取default的cache:from django.core.cache import cache
,
通过别名获取自定义的cache:
from django.core.cache import caches
# ...
cache = caches['memcached']
cache.get_or_set('key_word', key_word, 60*15)
基本的用法set(key, value, timeout) ,get(key)和add(key, new_value):
>>> cache.set('add_key', 'Initial value')
>>> cache.add('add_key', 'New value')
>>> cache.get('add_key')
'Initial value'
timeout设置为None时,缓存永不过时,设置为0时不缓存。
设置Vary header
Django默认是使用url地址作为cache的key值的,也就是对相同的url请求会返回相同版本的缓存,不论无论用户代理差异如cookie或语言首选项如何。设置Vary首部字段,可以选择不同的缓存。
from django.views.decorators.vary import vary_on_headers
@vary_on_headers('User-Agent')
def my_view(request):
...
对于不同的User-Agent
,my_view都会有不同的缓存。
也可以传多个headers:
@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
...
因为cookie
是常用的,所以可以使用django.views.decorators.vary.vary_on_cookie()
装饰器
@vary_on_cookie
def my_view(request):
...
@vary_on_headers('Cookie')
def my_view(request):
...
控制缓存
有关缓存的问题是数据的隐私性以及数据在级联缓存中存储。用户通常面临两种缓存:公共缓存和私有缓存时,Web应用程序需要一种方法来告诉缓存哪些数据是私有的,哪些是公共的。设置对特定的用户提供缓存服务:
from django.views.decorators.cache import cache_control
@cache_control(private=True)
def my_view(request):
...
还可以使用never_cache
装饰器,控制视图不被浏览器或其他缓存系统缓存。
from django.views.decorators.cache import never_cache
@never_cache
def myview(request):
...
Memcached
Memcached是Django支持的最快,最有效的缓存类型,它是一一个自由开源的、高性能的、分布式内存对象缓存系统。本质上,它是一个简洁的key-value存储系统。
Memcached作为守护进程运行,并分配了指定数量的RAM。 它所做的就是提供一个快速接口,用于在缓存中添加,检索和删除数据。 所有数据都直接存储在内存中,因此不会产生数据库或文件系统使用的开销。
安装
Linux系统安装memcached,首先要先安装libevent库。
Debian/Ubuntu: apt-get install libevent-dev
edhat/Centos: yum install libevent-devel
自动安装sudo apt-get install memcached
安装Memcached后,还要安装python相关的依赖库,最常用的python-memcached
和pylibmc
,pip install python-memcachedl
或pip install pylibmc
。pylic
是使用c语言编写的,需要有gcc和python-dev环境和memcached的依赖,安装方法。
Memcached 运行
Memcache 的命令帮助:
ulysses@ulysses:~$ memcached -h
启动选项:
-d
是启动一个守护进程;-m
是分配给Memcache使用的内存数量,单位是MB;-u
是运行Memcache的用户;-l
是监听的服务器IP地址,可以有多个地址;-p
是设置Memcache监听的端口,,最好是1024以上的端口;-c
是最大运行的并发连接数,默认是1024;-P
是设置保存Memcache的pid文件。-v
显示错误或警告,-vv
命令或回应信息也显示
关闭Memcache,使用kill命令结束进程:
kill `cat /tmp/memcached.pid`,注意使用反引号。
可以选择
- 作为前台程序启动
ulysses@ulysses:~$ memcached -p 11211 -m 64m -vv -P /tmp/memcached.pid
slab class 1: chunk size 96 perslab 10922
slab class 2: chunk size 120 perslab 8738
slab class 3: chunk size 152 perslab 6898
......
slab class 38: chunk size 394840 perslab 2
slab class 39: chunk size 524288 perslab 2
<26 server listening (auto-negotiate)
- 作为后台程序运行,使用
-d
参数
ulysses@ulysses:~$ memcached -p 11211 -m 64m -d -P /tmp/memcached.pid
连接Memcached服务:telnet ip port
ulysses@ulysses:~$ telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
stats
STAT pid 31746
STAT uptime 7294
STAT time 1541667313
STAT version 1.5.6 Ubuntu
STAT libevent 2.1.8-stable
STAT pointer_size 64
STAT rusage_user 0.630103
...
使用Memcached作为Django缓存
要在Django使用Memcached,需要在CACHES
设置LOCATION
.
LOCATION
可以使用ip:port
的格式,也可以使用unix:path
的格式,path
就是Memcached Unix socket文件。
Memcached的一个出色功能是它能够在多个服务器上共享缓存。这意味着可以在多台计算机上运行Memcached守护程序,程序会将该组计算机视为单个缓存,而无需在每台计算机上复制缓存值。可以使用一个列表列出这些地址。
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
Memcached的一个出色功能是它能够在多个服务器上共享缓存。这意味着可以在多台计算机上运行Memcached守护程序,程序会将该组计算机视为单个缓存,而无需在每台计算机上复制缓存值。可以使用一个列表列出这些地址。
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
Memcached的缺点
Memcached的最后一点是基于内存的缓存有一个缺点:因为缓存的数据存储在内存中,如果服务器崩溃,数据将会丢失。 显然,内存不适用于永久数据存储,因此不要依赖基于内存的缓存作为唯一的数据存储。