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分割和拼接字符串
Nov 01 Python
python连接mysql数据库示例(做增删改操作)
Dec 31 Python
浅析Python基础-流程控制
Mar 18 Python
Python2随机数列生成器简单实例
Sep 04 Python
学习Python selenium自动化网页抓取器
Jan 20 Python
python3.4实现邮件发送功能
May 28 Python
详解python中的Turtle函数库
Nov 19 Python
对python中if语句的真假判断实例详解
Feb 18 Python
在Python中如何传递任意数量的实参的示例代码
Mar 21 Python
详解Python文件修改的两种方式
Aug 22 Python
Python3.9新特性详解
Oct 10 Python
最新pycharm安装教程
Nov 18 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 上一篇,下一篇文章实现代码与原理说明
2010/05/09 PHP
使ecshop模板中可引用常量的实现方法
2011/06/02 PHP
PHP多线程编程之管道通信实例分析
2015/03/07 PHP
jQuery源码分析之Event事件分析
2010/06/07 Javascript
javascript中的onkeyup和onkeydown区别介绍
2013/04/28 Javascript
jquery ui对话框实例代码
2013/05/10 Javascript
深入探究JavaScript中for循环的效率问题及相关优化
2016/03/13 Javascript
一种Javascript解释ajax返回的json的好方法(推荐)
2016/06/02 Javascript
AngularJS 指令详细介绍
2016/07/27 Javascript
微信小程序 火车票查询实例讲解
2016/10/17 Javascript
javascript中的后退和刷新实现方法
2016/11/10 Javascript
jQuery实现圣诞节礼物传送(花式轮播)
2016/12/25 Javascript
微信小程序 基础组件与导航组件详细介绍
2017/02/21 Javascript
laydate日历控件使用方法详解
2017/11/20 Javascript
React数据传递之组件内部通信的方法
2017/12/31 Javascript
详解各版本React路由的跳转的方法
2018/05/10 Javascript
Angular(5.2->6.1)升级小结
2018/12/27 Javascript
小程序扫描普通链接二维码跳转小程序指定界面方法
2019/05/07 Javascript
JavaScript代码简化技巧实例解析
2020/09/09 Javascript
[16:14]教你分分钟做大人:米拉娜(HEROS)
2014/11/24 DOTA
[05:16]《大圣!大圣》——DOTA2新英雄齐天大圣配音李世宏老师专访
2016/12/13 DOTA
Python类的多重继承问题深入分析
2014/11/09 Python
Ubuntu下升级 python3.7.1流程备忘(推荐)
2018/12/10 Python
详解python持久化文件读写
2019/04/06 Python
给 TensorFlow 变量进行赋值的方式
2020/02/10 Python
Python 使用双重循环打印图形菱形操作
2020/08/09 Python
Python自动化测试基础必备知识点总结
2021/02/07 Python
HTML5 Canvas标签使用收录
2009/07/07 HTML / CSS
使用html2canvas实现将html内容写入到canvas中生成图片
2020/01/03 HTML / CSS
欧洲最大的品牌水上运动服装和设备在线零售商:Wuituit Outlet
2018/05/05 全球购物
极度干燥澳大利亚官方网站:Superdry澳大利亚
2019/03/28 全球购物
工程专业求职自荐书范文
2014/02/18 职场文书
小学生操行评语大全
2014/04/22 职场文书
中学教师师德师风承诺书
2015/04/28 职场文书
详解Nginx启动失败的几种错误处理
2021/04/01 Servers
第四次工业革命,打工人与机器人的竞争
2022/04/21 数码科技