Django缓存系统实现过程解析


Posted in Python onAugust 02, 2019

在动态网站中,用户每次请求一个页面,服务器都会执行以下操作:查询数据库,渲染模板,执行业务逻辑,最后生成用户可查看的页面。

这会消耗大量的资源,当访问用户量非常大时,就要考虑这个问题了。

缓存就是为了防止重复计算,把那些消耗了大量资源的结果保存起来,下次访问时就不用再次计算了。缓存的逻辑:

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和基于浏览器的缓存,这些类型的缓存你不直接控制,但是你可以提供给他们站点哪部分应该被缓存和怎样被缓存(通过HTTP headers)。

设置缓存

在settings中的CACHES中设置缓存,下面是几个可用的缓存选项:

Memcached

Django目前原生支持的最快最有效的缓存系统。要使用Memcached,需要下载Memcached支持库,一般是python-memcached或者pylibmc。

然后设置BACKEND为django.core.cache.backends.memcached.MemcachedCache(使用python-memcached时)或者django.core.cache.backends.memcached.PyLibMCCache(使用pylibmc时)。

设置LOCATION为ip:port或者unix:path。例如:

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',
 }
}

当使用pylibmc时,去掉unix:/前缀:

CACHES = {
 'default': {
  'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache',
  'LOCATION': '/tmp/memcached.sock',
 }
}

还可以在多台机器上运行Memcached进程,程序将会把这组机器当作一个单独的缓存,而不需要在每台机器上复制缓存值:

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',
  ]
 }
}

由于Memcached是基于内存的缓存,数据只存储在内存中,如果服务器死机的话数据会丢失,所以不要把内存缓存作为唯一的数据存储方法。

Database caching

Django也可以把缓存数据存储在数据库中。

CACHES = {
 'default': {
  'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
  'LOCATION': 'my_cache_table',
 }
}

LOCATION为数据库中table的名字,任意起,在数据库中未被使用过即可以。

创建cache table:

python manage.py createcachetable

使用多数据库时,也需要为cache table写Router:

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

Filesystem caching

也可以使用文件来存储缓存数据。

CACHES = {
 'default': {
  'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
  'LOCATION': '/var/tmp/django_cache',
 }
}

LOCATION为缓存数据存储目录。

windows中:

CACHES = {
 'default': {
  'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
  'LOCATION': 'c:/foo/bar',
 }
}

Local-memory caching

Django默认使用的缓存系统,数据存储在本地内存中:

CACHES = {
 'default': {
  'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
  'LOCATION': 'unique-snowflake',
 }
}

Dummy caching (for development)

开发时使用的:

CACHES = {
 'default': {
  'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
 }
}

Using a custom cache backend

也可以使用其它的缓存系统,比如Redis,django-redis地址https://github.com/niwinz/django-redis 。

下载:pip install django-redis

设置:

CACHES = {
 "default": {
  "BACKEND": "django_redis.cache.RedisCache",
  "LOCATION": "redis://127.0.0.1:6379/1",
  "OPTIONS": {
   "CLIENT_CLASS": "django_redis.client.DefaultClient",
  }
 }
}

Cache arguments

CACHES设置中有几个额外的参数:

TIMEOUT:缓存超时时间,默认为300s,可以设置为None,即永不超时。

OPTIONS : locmem, filesystem和database缓存系统这些有自己的剔除策略的系统有以下的参数:

MAX_ENTRIES : 缓存中存放的最大条目数,大于这个数时,旧的条目将会被删除,默认为300.

CULL_FREQUENCY:当达到MAX_ENTRIES的时候,被接受的访问的比率。实际的比率是1/cull_frequency,所以设置为2就是在达到max_entries时去除一半数量的缓存,设置为0意味着达到max_entries时,缓存将被清空。这个值默认是3。

KEY_PREFIX:一个会自动列入缓存key值的的字符串。

VERSION:缓存key值生成时使用的版本数字。

KEY_FUNCTION:key值最终生成所使用的方法。

缓存网站

要缓存整个网站,首先添加两个中间件:

MIDDLEWARE = [
 'django.middleware.cache.UpdateCacheMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.cache.FetchFromCacheMiddleware',
]

注意update中间件要放在首位,fetch中间件放在最后。

在settings中添加以下值:

  • CACHE_MIDDLEWARE_ALIAS:存储用的缓存别名
  • 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()禁止缓存

缓存页面

使用django.views.decorators.cache.cache_page()来缓存某个页面:

from django.views.decorators.cache import cache_page
 
@cache_page(60 * 15)
def my_view(request):
 ...

60*15是缓存15分钟。

cache参数可以设置使用CACHES中的哪一个cache系统,默认是default:

@cache_page(60 * 15, cache="special_cache")
def my_view(request):
 ...

key_prefix参数和CACHE_MIDDLEWARE_KEY_PREFIX设置起得作用相同:

@cache_page(60 * 15, key_prefix="site1")
def my_view(request):
 ...

可以在url中使用此方法:

from django.views.decorators.cache import cache_page
urlpatterns = [
 url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
]

模板片段缓存

{% load cache %}
{% cache 500 sidebar %}
 .. sidebar ..
{% endcache %}

{% cache %}模板标签会缓存block内容,至少包括两个参数:缓存时间和缓存片段的name。

可以根据变化的动态数据为一个片段缓存不同的copies:

{% load cache %}
{% cache 500 sidebar request.user.username %}
 .. sidebar for logged in user ..
{% endcache %}

CACHE API

根据CACHES设置中的cache别名获取cache系统:

>>> from django.core.cache import caches
>>> cache1 = caches['myalias']
>>> cache2 = caches['myalias']
>>> cache1 is cache2
True

获取默认default的cache:

>>> from django.core.cache import cache

基本用法set(key, value, timeout) 和get(key)::

>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
'hello, world!'

key为字符串,value为 picklable的python对象。timeout设置为None时,缓存永不过时,设置为0时不缓存。

设置Vary header

Django默认是使用url地址作为cache的key值的,也就是对相同的url请求会返回相同的缓存。如果想根据不同的请求首部字段(比如cookie, language, user-agent)缓存不同的内容,可以设置Vary首部字段。

from django.views.decorators.vary import vary_on_headers
 
@vary_on_headers('User-Agent')
def my_view(request):
 ...

上述代码会为不同的user-agent设置单独的缓存。也可以传多个headers:

@vary_on_headers('User-Agent', 'Cookie')
def my_view(request):
 ...

上述代码当user-agent和cookie都相同时才会有相同的缓存。

cookie是比较常用的,有单独的装饰器,下面代码是相通的:

@vary_on_cookie
def my_view(request):
 ... 
@vary_on_headers('Cookie')
def my_view(request):
 ...

Cache-Control头部

可以使用cache_control装饰器来设定Cache-Control头部。

设置对特定的用户提供缓存服务:

from django.views.decorators.cache import cache_control
@cache_control(private=True)
def my_view(request):
 ...

设置时间:

from django.views.decorators.cache import cache_control
@cache_control(max_age=3600)
def my_view(request):
 ...

等等,可用的Cache-Control指令(IANA registry)都可使用。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python 获取et和excel的版本号
Apr 09 Python
python分析nignx访问日志脚本分享
Feb 26 Python
Python中在脚本中引用其他文件函数的实现方法
Jun 23 Python
Python递归函数定义与用法示例
Jun 02 Python
Python自动化运维_文件内容差异对比分析
Dec 13 Python
对Pandas DataFrame缺失值的查找与填充示例讲解
Nov 06 Python
Python 保存矩阵为Excel的实现方法
Jan 28 Python
浅谈pycharm导入pandas包遇到的问题及解决
Jun 01 Python
Python 字符串池化的前提
Jul 03 Python
Python基于traceback模块获取异常信息
Jul 23 Python
python 无损批量压缩图片(支持保留图片信息)的示例
Sep 22 Python
python自动生成sql语句的脚本
Feb 24 Python
tensor和numpy的互相转换的实现示例
Aug 02 #Python
Django文件存储 自己定制存储系统解析
Aug 02 #Python
使用pycharm在本地开发并实时同步到服务器
Aug 02 #Python
Django文件存储 默认存储系统解析
Aug 02 #Python
Django 迁移、操作数据库的方法
Aug 02 #Python
Django用户认证系统 组与权限解析
Aug 02 #Python
python3中eval函数用法使用简介
Aug 02 #Python
You might like
php文件打包 下载之使用PHP自带的ZipArchive压缩文件并下载打包好的文件
2012/06/13 PHP
PHP生成器简单实例
2015/05/13 PHP
简单实现PHP留言板功能
2016/12/21 PHP
微信公众平台开发教程③ PHP实现微信公众号支付功能图文详解
2019/04/10 PHP
Javascript 解疑
2009/11/11 Javascript
使用jquery解析XML示例代码
2014/09/05 Javascript
JQuery简单实现锚点链接的平滑滚动
2015/05/03 Javascript
通过XMLHttpRequest和jQuery实现ajax的几种方式
2015/08/28 Javascript
详解javascript事件冒泡
2016/01/09 Javascript
浅析JS动态创建元素【两种方法】
2016/04/20 Javascript
AngularJS 模块详解及简单实例
2016/07/28 Javascript
js实现一键复制功能
2017/03/16 Javascript
AngularJS自定义指令详解(有分页插件代码)
2017/06/12 Javascript
Vue引用Swiper4插件无法重写分页器样式的解决方法
2018/09/27 Javascript
简单实现vue中的依赖收集与响应的方法
2019/02/18 Javascript
Python实现生成简单的Makefile文件代码示例
2015/03/10 Python
基于Python实现文件大小输出
2016/01/11 Python
python 实现求解字符串集的最长公共前缀方法
2018/07/20 Python
对pycharm 修改程序运行所需内存详解
2018/12/03 Python
Python使用paramiko操作linux的方法讲解
2019/02/25 Python
通过cmd进入python的实例操作
2019/06/26 Python
python框架Django实战商城项目之工程搭建过程图文详解
2020/03/09 Python
tensorflow模型转ncnn的操作方式
2020/05/25 Python
CSS3制作日历实现代码
2012/01/21 HTML / CSS
一款纯css3实现的颜色渐变按钮的代码教程
2014/11/12 HTML / CSS
印度首选时尚目的地:Reliance Trends
2018/01/17 全球购物
GetYourGuide台湾:预订旅游活动、景点和旅游项目
2019/06/10 全球购物
C语言笔试题回忆
2015/04/02 面试题
岗位职责说明书
2014/05/07 职场文书
质量在我心中演讲稿
2014/09/02 职场文书
统计工作个人总结
2015/03/03 职场文书
任命通知范文
2015/04/21 职场文书
升学宴祝酒词
2015/08/11 职场文书
勤俭节约主题班会
2015/08/13 职场文书
Vue中插槽slot的使用方法与应用场景详析
2021/06/08 Vue.js
idea以任意顺序debug多线程程序的具体用法
2021/08/30 Java/Android