概述:Django缓存机制介绍
Django缓存机制介绍 1.什么是缓存 缓存是一类可以更快地读取数据的介质统称,亦指其它可以加快数据读取的存储方式,通常用来存储临时数据,常用介质的是读取速度很快的内存。一般来说从数据库多次把所需要的数据提取出来,要比从内存或者硬盘等一次读出来付出的成本大很多。对于中大型网站而言,使用缓存减少对数据库的访问次数可以提升网站性能。
2.为什么使用缓存 以Django为例,当用户请求到达视图(View)后,视图会先从数据库提取数据放到模板中进行动态渲染,渲染后的结果就是用户看到的网页。如果用户每次请求都从数据库提取数据并渲染,将极大降低性能,不仅服务器压力大,而且客户端也无法即时获得响应。如果能将渲染后的结果放到速度更快的缓存中,每次有请求过来,先检查缓存中是否有对应的资源,如果有,直接从缓存中取出来返回响应,节省取数据和渲染的时间,不仅能大大提高系统性能,还能提高用户体验。
缓存的优势:降低服务器负载、避免重复计算工作、提升系统性能
3.Django缓存机制架构图
4.缓存方式 (1).开发调试 Dummy caching (for development) ,假缓存,仅开发过程中使用,以调试缓存接口,数据并没有真正缓存。
1 2 3 4 5 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', } }
(2).本地内存缓存 Local-memory caching,使用系统内存缓存。
1 2 3 4 5 6 CACHES = { 'default' : { 'BACKEND' : 'django.core.cache.backends.locmem.LocMemCache' , 'LOCATION' : 'unique-snowflake' , } }
(3).文件系统缓存 Filesystem caching,文件系统缓存,将缓存会把键值存储到独立的文件。
1 2 3 4 5 6 7 CACHES = { 'default' : { 'BACKEND' : 'django.core.cache.backends.filebased.FileBasedCache' , 'LOCATION' : '/var/tmp/django_cache' , } }
(4).数据库缓存 Database caching,数据库缓存,这个指把数据缓存到数据表。
1 2 3 4 5 6 CACHES = { 'default' : { 'BACKEND' : 'django.core.cache.backends.db.DatabaseCache' , 'LOCATION' : 'my_cache_table' , } }
(预先准备)创建数据缓存表:
1 python manage.py createcachetable [my_cache_table_name]
(5).Memcache缓存 Memcached,完全基于内存的缓存服务器,是Django本地支持的最快速,最高效的高速缓存类型,最初开发用于处理LiveJournal.com的高负载,随后由Danga Interactive开源,Facebook和维基百科等网站使用它来减少数据库访问并显着提高网站性能。它用于动态Web应用以减轻数据库负载从而显著提供网站性能,也是django中到目前为止最有效率的可用缓存。
特性:本身不支持持久化,不支持分布式。
缺点:缓存的数据存储在内存中,当服务器崩溃会导致数据丢失。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 CACHES = { 'default' : { 'BACKEND' : 'django.core.cache.backends.memcached.MemcachedCache' , 'LOCATION' : '10.10.10.10:11211' , } } CACHES = { 'default' : { 'BACKEND' : 'django.core.cache.backends.memcached.MemcachedCache' , 'LOCATION' : 'unix:/tmp/memcached.sock' , } } CACHES = { 'default' : { 'BACKEND' : 'django.core.cache.backends.memcached.MemcachedCache' , 'LOCATION' : [ ('10.10.10.10:11211' , 10 ), ('10.10.10.11:11211' , 20 ), ] } }
1 2 'BACKEND' : 'django.core.cache.backends.memcached.PyLibMCCache'
(6).自定义缓存 Using a custom cache backend,自定义缓存后端,比如使用Redis。
1 2 3 4 5 CACHES = { 'default' : { 'BACKEND' : 'path.to.backend' , } }
缓存(Cache)其它参数
参数名
参数关键字
含义
TIMEOUT
用于缓存的默认超时时间(以秒为单位),默认是300秒,当值为0将导致密钥立即过期(实际上“不缓存”)
OPTIONS
MAX_ENTRIES
缓存允许的最大条目数,超过这个数则旧值会被删除,默认是300。
OPTIONS
CULL_FREQUENCY
当达到MAX_ENTRIES时被删除的条目比例,实际比率是1/CULL_FREQUENCY,默认是3。
OPTIONS
KEY_PREFIX
缓存key的前缀,默认为空。
OPTIONS
VERSION
缓存key的版本,默认为1。
OPTIONS
KEY_FUNCTION
生成key点函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CACHES = { 'default' : { 'BACKEND' : 'django.core.cache.backends.filebased.FileBasedCache' , 'LOCATION' : os.path.join(BASE_DIR, "cache" ), 'TIMEOUT' : 300 , 'OPTIONS' :{ 'MAX_ENTRIES' : 300 , 'CULL_FREQUENCY' : 3 , }, 'KEY_PREFIX' : '' , 'VERSION' : 1 , 'KEY_FUNCTION' : function, } }
5.缓存粒度 (1).Per-site cache 1 2 3 4 5 MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware' , 'django.middleware.cache.FetchFromCacheMiddleware' , ]
(2).Per-view cache views.py
1 2 3 4 5 6 7 from django.views.decorators.cache import cache_pageimport time@cache_page(10 ) def cache (req ): time_now = time.time() return render(req, "cache.html" , locals ())
cache.html
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <title>Title</title> </head> <body> {{ time_now }} </body> </html>
(3).Template fragment caching views.py
1 2 3 4 5 import timedef cache2 (req ): time_now = time.time() return render(req, "cache2.html" , locals ())
cache.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 {% load cache %} <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <title>Title</title> </head> <body> {{ time_now }} <hr> {% cache 5 k1 %} {{ time_now }} {% endcache %} </body> </html>
1 其中{% cache cache_time cache_key %} cache_value {% endcache %}中,cache_value为填入的缓存内容,cache_time是缓存的时间,以秒为单位。
(4).Low-level cache API 缓存修改较少的变量,包括:字符串、字典、模型对象等
1 2 3 from django.core.cache import cachecache.set (key, value) myname = cache.get("myname" )
(5).Downstream cache 在用户请求抵达网站时预先进行缓存,例如ISP(互联网服务提供商)会缓存部分页面数据,该种方式可以极大地提升效率,但是存在一定的危险,由于部分页面内容信息存在敏感数据(基于身份验等),若盲目地保存页面数据可能暴露给其他访问者,不过可以通过设置来限制缓存机制对指定的页面数据的缓存。
6.Redis相关操作 (1)下载&安装 a.下载
https://redis.io/download
b.安装
1 2 3 4 5 6 7 8 # 启动终端 tar -zxvf redis-6 .0 .5 .tar.gz sudo cp -rf redis-6 .0 .5 /usr/local/ cd /usr/local/redis-6 .0 .5 sudo make test sudo make install sudo mkdir bin etc db sudo cp src/mkreleasehdr.sh src/redis-benchmark src/redis-check-rdb src/redis-cli src/redis-server bin/
(2)启动服务&客户端 a.启动服务:redis-server
b.启动客户端:redis-cli
(需要重新开启一个新的命令行窗口)
(3)查看Redis信息状态
包含以下信息:
server
:服务器信息;
clients
:已连接客户端信息;
memory
:内存信息;
persistence
:持久化信息;
stats
:一般统计信息
replication
:主/从复制信息
CPU
: CPU 的计算量统计信息
cluster
:集群相关信息
keyspace
:数据库相关的统计信息
(4)支持的五种数据类型及操作
string(字符串)
hash(哈希)
list(列表)
set(集合)
zset(有序集合)
(5)官方命令帮助文档
keys *
:查看所有key的内容
exists key
:查看key是否存在
flushall
:清空所有信息
……
https://redis.io/commands
(6)python连接redis a.安装
b.传统连接
1 2 3 4 5 import redisconn=redis.Redis(host='127.0.0.1' ,port=6379 ,password='pwd' ) conn.set ('name' ,'jimmy' ) name=conn.get('name' ) print (name)
c.连接池连接
redis_pool.py
1 2 import redispool = redis.ConnectionPool(host='127.0.0.1' , port=6379 , password='pwd' , max_connections=1000 )
file.py
1 2 3 4 5 6 7 8 9 10 11 12 13 import redisfrom redis_pool import poolwhile True : key=input ("请输入key:" ) val=input ("请输入val:" ) conn = redis.Redis(connection_pool=pool) conn.set (key, val) pipe = r.pipeline(transaction=True ) pipe.set ('name' ,'ale' ) pipe.set ('role' ,'ab' ) pipe.execute()
(7)基本命令 keys - 查看所有key的内容
exists key - 查看key是否存在
set key value - 设置key的内容
get key - 获取key的内容
flushall - 清空所有信息
(8)可视化工具
medis-gui-for-redis 链接
Redis桌面管理工具 链接
(9)查看端口/进程 查看端口:lsof -i:6379
查看进程:ps -ef | grep redis
7.django-redis的配置 (1)安装 1 pip install django-redis
(2)settings配置 a.使用Redis缓存网站数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 REDIS_DEFAULT_LOCATION = env('REDIS_DEFAULT_LOCATION' , 'redis://127.0.0.1:6379/1' ) CACHE_DEFAULT_TIMEOUT = env('REDIS_DEFAULT_TIMEOUT' , 7 * 24 * 60 * 60 ) ''' default是连接池的名称(名称可以修改,还可以在往后面加连接池) ''' CACHES = { "default" : { "BACKEND" : "django_redis.cache.RedisCache" , "LOCATION" : REDIS_DEFAULT_LOCATION, 'TIMEOUT' : CACHE_DEFAULT_TIMEOUT, "OPTIONS" : { "CLIENT_CLASS" : "django_redis.client.DefaultClient" , "CONNECTION_POOL_KWARGS" : {"max_connections" : 100 }, "PASSWORD" : "pwd" , } } }
配置好后可进行测试缓存是否成功:
1 2 python manager.py shell > >> cache.set("key" ,"value" )
b.缓存Celery
1 2 3 4 5 CELERY_URL=redis://127.0 .0 .1 :6379 CELERY_BROKER_URL=redis://127.0 .0 .1 :6379 /1 CELERY_RESULT_BACKEND=redis://127.0 .0 .1 :6379 /2
c.使用Redis缓存Channel layers
1 2 3 4 5 6 7 8 9 CHANNEL_LAYERS = { "default" :{ "BACKEND" : "channels_redis.core.RedisChannelLayer" , "CONFIG" :{ "hosts" :[f'{env("REDIS_URL" ,default="redis://127.0.0.1:6379" )} /3' ], } } }
d.激活压缩(默认:关闭)
1 2 3 4 5 6 7 8 CACHES = { "default" : { "OPTIONS" : { "COMPRESSOR" : "django_redis.compressors.zlib.ZlibCompressor" , } } }
(3)不同级别的缓存 视图指定部分缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 import redisfrom django.shortcuts import render,HttpResponsefrom django_redis import get_redis_connection def index (request ): conn=get_redis_connection('default' ) conn.set ("name" ,"egon" ) return HttpResponse("设置成功!" ) def order (request ): conn=get_redis_connection('default' ) name=conn.get("name" ) return HttpResponse(name)
全站缓存
1 2 3 'django.middleware.cache.UpdateCacheMiddleware' ...其他中间件 'django.middleware.cache.FetchFromCacheMiddleware'
单视图缓存
1 2 3 4 5 6 from django.views.decorators.cache import cache_page@cache_page(60 *15 ) def index (request ): ctime=str (time.time()) return HttpResponse(ctime)
Django Debug Toolbar 不仅仅可以评估Django中的Cache性能,还可以监控 SQL语句操作、CPU 运行情况、静态文件使用情况、模板使用情况等,提供了前端面板,操作简单方便。
(1)安装及配置 a.下载依赖库
1 pip install django-debug-toolbar
b.修改配置信息(settings.py
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 DEBUG = True INSTALLED_APPS = ( ...... 'debug_toolbar' , ) MIDDLEWARE = [ 'debug_toolbar.middleware.DebugToolbarMiddleware' , ...... ]
c.配置URL(urls.py
)
1 2 3 4 5 if settings.DEBUG: import debug_toolbar urlpatterns = [ url(r'^__debug__/' , include(debug_toolbar.urls)), ] + urlpatterns
其它配置参考文档:链接
Django Debug Toolbar 中的 Cache 部分提供了以下信息:
总次数
总耗时
命中次数
未命中次数
各操作执行次数
具体请求的耗时、类型、参数、关键字参数、Backend
(2)动手实践 操作系统:macOS Catalina
内存: 16GB 2667 MHz DDR4
缓存方式:自定义缓存——redis;
缓存粒度:Low level cache API
实践代码:
a.安装
1 pip install django-redis
b.CACHES设置
1 2 3 4 5 6 7 8 9 10 CACHES = { "default" : { "BACKEND" : "django_redis.cache.RedisCache" , "LOCATION" : 'redis://127.0.0.1:6379' , 'TIMEOUT' : 60 , "OPTIONS" : { "CLIENT_CLASS" : "django_redis.client.DefaultClient" , } } }
c.视图中进行缓存
1 2 3 4 5 6 7 8 from django.core.cache import cacheif cache.get("age" ): print (cache.get("age" )) else : cache.set ("age" , 18 ) cache.get("name" ) cache.get("age" )
d.实践效果
从实践效果图发现第一次读取耗时较后续读写操作更多,主要原因是django-redis
默认使用Django setting 中 DJANGO_REDIS_CONNECTION_FACTORY
参数指定,在没提供该参数的前提下默认使用 django_redis.pool.ConnectionFactory
类产生连接:
1 2 3 4 5 6 7 8 9 10 11 def get_connection_factory (path=None , options=None ): if path is None : path = getattr ( settings, "DJANGO_REDIS_CONNECTION_FACTORY" , "django_redis.pool.ConnectionFactory" , ) cls = import_string(path) return cls(options or {})
其中连接池的连接方式保证了数据库的连接得以重用,避免了较为频繁的创建与释放连接导致的大量性能开销,减少系统消耗的基础并提升了系统运行环境的平稳性,减少内存碎片和数据库临时进程和线程的数量。
因而第一次请求耗时较长的原因包含了连接耗时,后续的读写请求将重用数据库的连接,将耗时大大降低。
8.python 连接 redis 安装
连接
1 2 3 4 5 6 7 import redisconn=redis.Redis(host='127.0.0.1' ,port=6379 ,password='pwd' ) conn.set ('name' ,'jimmy' ) name=conn.get('name' ) print (name)
与传统方式连接不同之处:链接不断开且长时间保持连接
1 2 3 4 import redispool=redis.ConnectionPool(host='127.0.0.1' ,port=6379 ,password='pwd,max_connections=1000) conn=redis.Redis(connection_pool=pool) conn.set(' foo',' Bar')
redis_pool.py
1 2 import redispool = redis.ConnectionPool(host='127.0.0.1' , port=6379 , password='pwd' , max_connections=1000 )
file.py
1 2 3 4 5 6 7 import redisfrom redis_poll import poolwhile True : key=input ("请输入key:" ) val=input ("请输入val:" ) conn = redis.Redis(connection_pool=pool) conn.set (key, val)
处理字典数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 import redispool=redis.ConnectionPool(host='47.99.191.149' ,port=6379 ,password="cyy520" ,max_connections=1000 ) conn=redis.Redis(connection_pool=pool) conn.hset('k1' ,'username' ,'alex' ) conn.hset('k1' ,'age' ,18 ) conn.hmset('k2' ,{'username' : 'jim' , 'age' : '19' }) ''' redis={ k1:{ username:alex, age:18 } } ''' val=conn.hget('k1' ,'username' ) print (val)val2=conn.hmget('k2' ,'username' ,'age' ) vals=conn.hgetall('k1' ) print (vals) ret = conn.hscan_iter('k1' ,count=100 ) for item in ret: print (item)
事务操作 1 2 3 4 5 6 7 pipe=conn.pipeline(transaction=True ) pipe.multi() pipe.set ('k1' ,123 ) pipe.hset('k2' ,'num' ,666 ) pipe.execute()
:paperclip:问题及解决方法 (1)Redis编译过程报错 Q:执行sudo make test
编译过程可能报错
A:执行命令清理再编译:sudo make distclean && sudo make && sudo make test
(2)Redis存储过程失败 Q:存储过程中出现“Failed opening the RDB file dump.rdb (in server root dir /usr/local/redis-6.0.5) for saving: Permission denied”
A:未赋予权限,需要对所安装的redis目录给予777权限:sudo chmod -R 0777 redis-6.0.5
其它补充参考 redis cookbook
redis五大难题解决方法:雪崩,穿透,并发
微服务架构下的缓存系统设计
互联网架构中缓存介绍
最全面的缓存架构设计
Redis集群
Install and Configures Redis server instances
A Django Channels channel layer
使用第三方组件(django-redis)创建连接池
redis python client