Python Web之路Django(五):缓存架构

Python 2017-02-27

起步

django的缓存架构非常灵活,提供了不同层次的缓存粒度,甚至可以是整个站点。而且令人惊讶的是对已有界面添加缓存只要修改一点点。

设置缓存方式

有很多种缓存方式,文件缓存啦,数据库缓存啦,本地缓存,第三方缓存等,这些都需要在 setting.py 中的 CACHES 配置缓存:

memcached 方式

Memcached 是一个高性能的分布式内存对象缓存系统。python使用它需要安装 python-memcached 依赖。 并且支持分布式缓存服务器:

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': [
            '172.19.26.240:11211',
            '172.19.26.242:11211',
            '172.19.26.244:11213',
        ]
        # 'LOCATION': '127.0.0.1:11211', # 如果只有单台

    }
}

使用socket例子:

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

memchache的缓存完全是在内存中的,也就是,服务器一旦崩溃或重启,所有数据都不复存在,因此,决不能将缓存作为唯一的数据存储方式。不过都2017年,RAW和ROW的区别应该很普及了。

数据库缓存

这种缓存方式就不怕断电丢失数据了,建表:

python manage.py createcachetable [cache_table_name]

表名不要和其他冲突就行了,没什么需要注意的了。

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

文件缓存

文件路径需用 绝对路径,并且记得赋予读写权限。

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': '/var/tmp/django_cache',
        #'LOCATION': 'c:/foo/bar',#windows下的示例
    }
}

本地内存缓存

如果有内存有点,但没能力运行memcache,就可以考虑采用本地内存缓存,这个缓存是多进程和线程安全的。

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

缓存LOCATION用来区分每个内存存储,如果你只有一个本地内存缓存,你可以忽略这个设置;但如果你有多个的时候,你需要至少给他们中一个赋予名字以区分他们。

注意每个进程都有它们自己的私有缓存实例,所以跨进程缓存是不可能的,因此,本地内存缓存不是特别有效率的,建议你只是在内部开发测时使用,不建议在生产环境中使用。

虚拟缓存

不是真实的缓存,只是实现了缓存的接口而已。当如果你需要在开发和测试中不想使用缓存,但不想修改缓存相关的代码,这种情况就可以使用虚拟缓存了:

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

其他设置

有些缓存行为需要额外的参数来控制:

  • TIMEOUT:缓存的默认过期时间,以秒为单位, 这个参数默认是 300 seconds (5 分钟).
  • OPTIONS: 这个参数应该被传到缓存后端。有效的可选项列表根据缓存的后端不同而不同,由第三方库所支持的缓存将会把这些选项直接配置到底层的缓存库。
    • MAX_ENTRIES:高速缓存允许的最大条目数,超出这个数则旧值将被删除. 这个参数默认是300.
    • CULL_FREQUENCY:当达到MAX_ENTRIES 的时候,被删除的条目比率。 实际比率是 1 / CULL_FREQUENCY, 所以设置CULL_FREQUENCY 为2会在达到MAX_ENTRIES 所设置值时删去一半的缓存。这个参数应该是整数,默认为 3。 把 CULL_FREQUENCY的值设置为 0 意味着当达到MAX_ENTRIES时,缓存将被清空。
  • KEY_PREFIX:将自动包含(默认情况下预置为)Django服务器使用的所有缓存键的字符串。
  • VERSION:由Django服务器生成的缓存键的默认版本号。
  • KEY_FUNCTION包含函数的虚线路径的字符串,定义如何将前缀,版本和键组成最终缓存键。
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': '/var/tmp/django_cache',
        'TIMEOUT': 60,
        'OPTIONS': {
            'MAX_ENTRIES': 1000
        }
    }
}

非法的参数会被忽略掉。

使用缓存

django提供多层次缓存架构满足复杂的业务需求。

整站缓存

通过设置 MIDDLEWARE_CLASSES 完成缓存整个网站:

MIDDLEWARE_CLASSES += (
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
)

这里的顺序是很重要的,更新应在获取中间件之前。同时添加参数必要的参数到settings.py里:

CACHE_MIDDLEWARE_ALIAS = 'cache_alias' # 用于存储的缓存的别名
CACHE_MIDDLEWARE_SECONDS = 600 # 每个page需要被缓存多少秒.
CACHE_MIDDLEWARE_KEY_PREFIX = 'app' # 如果缓存被使用相同Django安装的多个网站所共享,那么把这个值设成当前网站名,或其他能代表这个Django实例的唯一字符串,以避免key发生冲突。 如果你不在意的话可以设成空字符串。

缓存视图

单页面缓存采用 cache_page 修饰器,接受一个参数: timeout,秒为单位:

from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request):
    ...

这样就相当于缓存15分钟。 或者在url配置里面设置缓存:

from django.views.decorators.cache import cache_page

urlpatterns = [
    url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
]

缓存模板片段

也可以缓存一部分的模板内容,通过 cache 标签完成的,标签 {% cache %} 将按给定的时间缓存包含块中的内容。它最少需要两个参数:缓存时间(以秒为单位);给缓存片段起的名称。 该名称将被视为是,不使用变量。例如:

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

有时候需要给不同用户缓存不同信息:

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

底层缓存API

底层接口:set(key, value, timeout)get(key):

>>> from django.core.cache import cache
>>> cache.set('my_key', 'hello, world!', 30)
>>> cache.get('my_key')
'hello, world!'

注意事项

缓存版本

缓存被保存的时候是带上可以带上版本号的,比如:

# Set version 2 of a cache key
>>> cache.set('my_key', 'hello world!', version=2)
# Get the default version (assuming version=1)
>>> cache.get('my_key')
None
# Get version 2 of the same key
>>> cache.get('my_key', version=2)
'hello world!'

利用版本这个特性,当我们需要保留一些数据摒除另外一些的时候,我们可以通过修改需要保留数据的版本,和设置新的 VERSION 来达到目的。可见这个特性适合用于新增功能或版本迭代时使用。

缓存键转换

当设置了 KEY_PREFIX 的前缀后,它在memcached中的键值会被转换,其实这个键值由前端和版本号组成,还用冒号做为连接:

def make_key(key, key_prefix, version):
    return ':'.join([key_prefix, str(version), smart_str(key)])

缓存警告

Memcached是最常用的生产缓存后端,它不允许超过 250 个字符或包含空格或控制字符的缓存键,并且使用这样的键将导致异常。

总结

django自带的缓存体系十分健壮,并且对于已开发的网站要添加缓存十分简单,多样的缓存方式从整站到底层调用,再配合那些“上游”“下游”缓存,基于浏览器的缓存等,可以给网站带来加速。


本文由 hongweipeng 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

赏个馒头吧