django 缓存框架
动态网站的基本原因就是每次请求都是动态的,有人在后台添加数据也会同时有人在前台读取数据,web的工作就是每次将数据层查询的数据渲染到前台给客户查看。
其实这里的缓存和CPU系统级别的缓存比较相似,对计算机架构比较清楚的会了解,CPU在读取内存的速度是大于读取磁盘的,而同时CPU进行内存交换的速度同样也有一定的延时,所以CPU引入了CPU缓存机制,针对20-80原则,这个缓存不需要很大,但是缓存的速度基本等同于CPU速度,所以可以提升整体性能。
网站缓存同样也是起到这个作用,数据库当前是存在磁盘上的,所以每次web去取值需要去到磁盘上取,这个性能由数据库性能和磁盘IO来决定。同样的根据80-20原则,大多数的请求实际上是相同的,所以我们可以将内容缓存到内存中,如果某请求在内存中已存在,就不需要重复的再去数据库查询。对于某些更新频率不高的网站,可能memcache可以解决基本所有的查询了。
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也能很好的配合那些“下游”缓存, 比如 Squid 和基于浏览器的缓存。
设置缓存位置
缓存同样可以存在不同的地方,可以存在数据库中,起到减少数据连接的效用。可以存在文件系统中,减少数据库的负担。也可以存在内存中,提高整体性能。不同的位置对于性能的区别是完全不同的。
Memchache
最高效的缓存类型, Memcached 是一个全部基于内存的缓存服务,起初是为了解决LiveJournal.com负载来开发的,后来是由Danga开源出来的。 它被类似Facebook 和 维基百科这种网站使用,用来减少数据库访问,显著的提高了网站的性能。
Memcached 是个守护进程,它被分配了单独的内存块。 它做的所有工作就是为缓存提供一个快速的添加,检索,删除的接口。 所有的数据直接存储在内存中,所以它不能取代数据库或者文件系统的使用。
需要在Django中使用Memcached时:
将 BACKEND 设置为django.core.cache.backends.memcached.MemcachedCache
或者 django.core.cache.backends.memcached.PyLibMCCache
(取决于你所选绑定memcached的方式)
将 LOCATION 设置为 `ip:port 值,ip 是 Memcached 守护进程的ip地址, port 是Memcached 运行的端口。或者设置为 unix:path 值,path 是 Memcached Unix socket file的路径.
memcache的几种绑定模式,本地端口绑定:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
socket绑定:
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:11212',
'172.19.26.244:11213',
]
}
}
Database caching 数据库级缓存
将缓存保存在数据库中。
数据库缓存配置
为了把数据表用来当做你的缓存后台:
把BACKEND设置为django.core.cache.backends.db.DatabaseCache
把 LOCATION 设置为 tablename
, 数据表的名称。这个名字可以是任何你想要的名字,只要它是一个合法的表名并且在你的数据库中没有被使用过。
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
}
}
创建缓存表
在使用之前需要使用django来创建缓存表:
python manage.py createcachetable
表名会从LOCATION中获取
多数据库缓存
如果你在多数据库的情况下使用数据库缓存,你还必须为你的数据库缓存表设置路由说明。
class CacheRouter(object):
"""A router to control all database cache operations"""
def db_for_read(self, model, **hints):
"All cache read operations go to the replica"
if model._meta.app_label == 'django_cache':
return 'cache_replica'
return None
def db_for_write(self, model, **hints):
"All cache write operations go to primary"
if model._meta.app_label == 'django_cache':
return 'cache_primary'
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
"Only install the cache model on primary"
if app_label == 'django_cache':
return db == 'cache_primary'
return None
如果你使用多数据库缓存, createcachetable会在每个缓存中创建一个表。
如果你使用多数据库,createcachetable会遵循你的数据库路由中的allow_migrate()方法
Filesystem caching 文件级缓存
为了使用文件缓存,需要配置两个参数:
把BACKEND配置成"django.core.cache.backends.filebased.FileBasedCache"
把LOCATION配置成相应的文件目录
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
}
}
如果是windown的话,需要加上盘符
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': 'c:/foo/bar',
}
}
Local-memory caching
如果没有设置其他的缓存方式,这种方式将会是setting.py中默认的缓存方式。这种缓存是每进程的,并且是线程安全的。
如果需要使用需要将BACKEND设置成 "django.core.cache.backends.locmem.LocMemCache"
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
如果有多个缓存系统的话,需要将LOCATION的名称区分出来。
Dummy caching
django 的 dummy caching实际上不是一个真实的缓存,他主要实现缓存的interface,但是不进行任何实际操作。
他的用处在于如果生产环境有一个叫重量的缓存,但是本地开发环境不希望缓存,但是你又不希望为这种情况修改代码,就可以使用dummy caching
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}
缓存配置参数
TIMEOUT: 缓存过期时间,以秒为准,默认为300秒
OPTIONS: 由第三方库所支持的缓存将会把这些选项直接配置到底层缓存库
MAX_ENTRIES
:高速缓存允许的最大条目数,超出这个数则旧值将被删除. 这个参数默认是300.CULL_FREQUENCY
:当达到MAX_ENTRIES
的时候,被删除的条目比率。 实际比率是1 / CULL_FREQUENCY
, 所以设置CULL_FREQUENCY
为2会删去一半的缓存MAX_ENTRIES
达到时。这个参数应该是整数,默认为 3. 把CULL_FREQUENCY
的值设置为 0 意味着当达到MAX_ENTRIES
时,缓存将被清空。某些缓存后端 (database尤其)这将以很多缓存丢失为代价,大大much 提高接受访问的速度。
KEY_PREFIX:将会自动保存到django服务器的所有缓存key中
VERSION:默认成本的而cache key版本。
KEY_FUNCTION:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',
'TIMEOUT': 60,
'OPTIONS': {
'MAX_ENTRIES': 1000
}
}
}
使用站点级缓存
一旦高速缓存设置,最简单的方法是使用缓存缓存整个网站。
MIDDLEWARE_CLASSES = (
'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.cache.FetchFromCacheMiddleware',
)
'update'中间件,必须放在列表的开始位置,而fectch中间件,必须放在最后。
然后,添加下面这些需要的参数到settings文件里:
CACHE_MIDDLEWARE_ALIAS
– 用于存储的缓存的别名
CACHE_MIDDLEWARE_SECONDS
–每个page需要被缓存多少秒.
CACHE_MIDDLEWARE_KEY_PREFIX
– 如果缓存被多个使用相同Django安装的网站所共享,那么把这个值设成当前网站名,或其他能代表这个Django实例的唯一字符串,以避免key发生冲突。 如果你不在意的话可以设成空字符串。
FetchFromCacheMiddleware
缓存GET和HEAD状态为200的回应,用不同的参数请求相同的url被视为独立的页面,缓存是分开的。
单个view缓存
更加轻巧的缓存方式,可以精确到每个view中。django为每个view定义了相关的装饰器
django.views.decorators.cache 定义了一个自动缓存视图响应的 cache_page装饰器
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...
60*15代表缓存的秒数。
和站点缓存一样,视图缓存与 URL 无关。如果多个 URL 指向同一视图,每个URL将会分别缓存。
urlpatterns = [
url(r'^foo/([0-9]{1,2})/$', my_view),
]
此案例中发送到_foo/1和 /foo/23会被分别缓存。但是一旦一个明确的 URL (e.g., /foo/23_) 已经被请求过了, 之后再度发出的指向该 URL 的请求将使用缓存
一些额外的参数
cache, 指示修饰符去具体使用缓存 (from your CACHES setting) 当要缓存页面结果时。
@cache_page(60 * 15, cache="special_cache")
def my_view(request):
...
key_prefix
@cache_page(60 * 15, key_prefix="site1")
def my_view(request):
...
在URL中使用cache
前面可以看到我们在view中添加了相关的装饰器来使用缓存,这样会导致view层和缓存强耦合。会导致在某些无缓存的站点中重用该视图的时候报错。
解决方法是将cache配置到url中:
from django.views.decorators.cache import cache_page
urlpatterns = [
url(r'foo_([0-9]{1,2})_$', cache_page(60 * 15)(my_view)),
]
模板片段缓存
可以使用cache模板标签来缓存模板的一个片段。使用 {% load cache %}
来指定缓存模板标签
标签{% cache %}将按给定的时间缓存包含块中的内容。
{% load cache %}
{% cache 500 sidebar %}
.. sidebar ..
{% endcache %}
缓存API
有时候,我们并不希望总是缓存完整的页面,这样实际上有时候效率比较低下
如果网页包含一个view,这个view依赖于几个比较复杂的查询,查询结果在不同的情况下都会变化。在这种情况的时候,使用整页caching结果肯定是不理想的。
在这种时候,django提供了更底层的缓存API,用这样的API能够更好并且更安全的cache数据。
可以参考:
>>> from django.core.cache import caches
>>> cache1 = caches['myalias']
>>> cache2 = caches['myalias']
>>> cache1 is cache2
True
>>> from django.core.cache import get_cache
>>> get_cache('default')
>>> get_cache('django.core.cache.backends.memcached.MemcachedCache', LOCATION='127.0.0.2')
>>> get_cache('default', TIMEOUT=300)
基本的使用方法:
>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
'hello, world!'
Cache key prefixing
如果你在多台服务器之间,或者在生产和开发环境之间共享cache实例,有可能出现一个数据在一台服务器上缓存,但是需要被另外一台使用。如果缓存数据在两台服务器上是不同的,将导致很多的严重问题。
为了解决这些问题,Django可以提供将可以给不同的服务器添加一个cache前缀,当出现这种情况的时候,django将自动为这些cache添加前缀。
Cache versioning
当运行时的缓存值状态改变的时候,我们需要清除并更新已经存在的cache值。最简单的方法当然是更新整个的缓存区,但是这肯定会导致很多依然可用的缓存丢失。
django为单独的缓存值提供了一个更好的方法,django缓存框架有一个系统记得版本控制器,使用VERSION标签来指定不同缓存。
# Increment the version of 'my_key'
>>> cache.incr_version('my_key')
# The default version still isn't available
>>> cache.get('my_key')
None
# Version 2 isn't available, either
>>> cache.get('my_key', version=2)
None
# But version 3 *is* available
>>> cache.get('my_key', version=3)
'hello world!'