对于Web服务来说,对客户端发来的每个请求都需要计算,如果在短时间内有多个相同的请求,就要进行多次不必要的重复计算,使用缓存能有效地解决这个问题,如果请求的值存在缓存中,就直接返回缓存的值而不需要计算,能够减少计算和时延。
缓存位置
缓存数据可以存放在数据库,文件系统或内存中,使用缓存服务器还能实现多个服务器共享缓存,参考官方文档在settings.py中配置缓存。
Django默认配置了本地缓存,该缓存是多进程的,且线程安全。
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake',
}
}
如果只有一本地内存缓存,可以省略LOCTION
配置,如果有多个,则至少要给其中一个配置LOCATION
以区分。
需要注意的是,Django是多进程部署时,每个进程都有自己私有的缓存实例,意味着跨缓存是不可能发生的,这样降低了命中率,而且如果其中一个进程修改了数据,其他进程的缓存没有更新之前就会返回错误的数据。
缓存粒度
Django提供了不同级别的缓存粒度,可以缓存整个站点、特定视图或部分难生成的内容,参考官方文档。
视图缓存
django.views.decorators.cache
定义了一个cache_page
装饰器,用于自动缓存视图的响应。
from django.views.decorators.cache import cache_page
@cache_page(60 * 15)
def my_view(request):
...
cache_page
的参数指定了缓存过期时间,以秒为单位。
如果多个URL指向相同的视图,每个URL将被单独缓存,如果URLconf是这样的:
urlpatterns = [
path('foo/<int:code>/', my_view),
]
那么/foo/1/
和/foo/23/
的请求将被分别缓存。
但这种给my_view
函数加装饰器的方法改变了函数,使my_view
函数难以复用,可以改成在URLconf中再加cache_page
装饰器。
from django.views.decorators.cache import cache_page
urlpatterns = [
path('foo/<int:code>/', cache_page(60 * 15)(my_view)),
]
任意级别粒度
Django提供了底层的缓存API,用于缓存任意级别粒度的对象,可以缓存任何可以安全的 pickle的Python对象:字符串、字典、列表,或者其他。
使用django.core.cache.cache
引用缓存,基本用法:
from django.core.cache import cache
# cache.set(key, value, timeout=DEFAULT_TIMEOUT, version=None)
cache.set('my_key', 'hello, world!', 30)
# cache.get(key, default=None, version=None)
cache.get('my_key')
# 'hello, world!'
# 30秒后'my_key'过期
cache.get('my_key')
# None
cache.get()
可以带一个默认参数。如果对象不在缓存中,将返回指定的值。
cache.get('my_key', 'has expired')
# 'has expired'
在键不存在的时候,使用add()
方法可以添加键。它与set()
带有相同的参数,但如果指定的键已经存在,将不会尝试更新缓存。
如想知道通过add()
存储的值是否在缓存中,可以检查返回值。如果值已保存,将返回 True
,否则返回False
。
# cache.add(key, value, timeout=DEFAULT_TIMEOUT, version=None)
cache.set('add_key', 'Initial value')
cache.add('add_key', 'New value')
cache.get('add_key')
# 'Initial value'
如果想得到键值或者如果键不在缓存中时设置一个值,可以使用get_or_set()
方法。它带有和get()
一样的参数,但默认是为那个键设置一个新缓存值,而不是简单返回:
# cache.get_or_set(key, default, timeout=DEFAULT_TIMEOUT, version=None)
cache.get('my_new_key') # returns None
cache.get_or_set('my_new_key', 'my new value', 100)
# 'my new value'
# 可以传递任何可调用的值作为默认值:
import datetime
cache.get_or_set('some-timestamp-key', datetime.datetime.now)
# datetime.datetime(2014, 12, 11, 0, 15, 49, 457920)
get_many()
接口,对多个值返回一个字典。
# cache.get_many(keys, version=None)
cache.set('a', 1)
cache.set('b', 2)
cache.set('c', 3)
cache.get_many(['a', 'b', 'c'])
# {'a': 1, 'b': 2, 'c': 3}
set_many()
传递键值对的字典,可以设置多个值。
# cache.set_many(dict, timeout)¶
cache.set_many({'a': 1, 'b': 2, 'c': 3})
cache.get_many(['a', 'b', 'c'])
# {'a': 1, 'b': 2, 'c': 3}
delete()
清除特定对象缓存。
# cache.delete(key, version=None)
cache.delete('a')
delete_many()
一次性清除多个键。
# cache.delete_many(keys, version=None)
cache.delete_many(['a', 'b', 'c'])
使用cache.clear()
可以删除缓存里的所有键。
cache.touch()
为键设置一个新的过期时间。
# cache.touch(key, timeout=DEFAULT_TIMEOUT, version=None)
cache.touch('a', 10)
# True
分别使用incr()
或decr()
可以递增或递减一个已经存在的键的值。是否为原子方法依赖于后端支持。
# cache.incr(key, delta=1, version=None)
# cache.decr(key, delta=1, version=None)
cache.set('num', 1)
cache.incr('num')
# 2
cache.incr('num', 10)
# 12
cache.decr('num')
# 11
cache.decr('num', 5)
# 6
如果缓存后端已经实现了close()
方法,可以用cache.close()
关闭和缓存的连接。