缓存
缓存--把高频读取的数据,防止到更快的存储介质里
一类可以更快的读取数据的介质同城,也指可以加快数据的读取的存储方式。一般用来存储临时数据,常用介质是读取速度很快的内存
场景:博客列表页,电商商品详情页,特点就是数据变动频率较少。
Django中缓存的用法
缓存方案
把数据库中大量的数据查询的结果,放在另一个库中,下次就可以很快从另一个库中拿到数据。
# 需要手动执行数据库命令,创建缓存表
# python manage.py createcachetable
# mysql
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
"LOCATION": "my_cache_table", # 随便起
"TIMEOUT":300, # 缓存保存时间 秒 默认值为300
"OPTIONS": {
"MAX_ENTRIES":300, # 缓存最大数据条数
"CULL_FREQUENCY":2, # 缓存条数达到最大值时,删除1/x的缓存数据
}
}
}
数据缓存到服务器内存中
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "unique-snowflake", # 随便起
}
}
存储到文件中
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
"LOCATION": "/var/tmp/django_cache", # 文件路径
}
}
使用
视图函数中整体缓存策略
from django.views.decorators.cache import cache_page # 整体缓存策略 @cache_page(30) # 秒 def my_view(request): pass
路由中整体缓存策略
from django.views.decorators.cache import cache_page urlpatterns = [ # 整体缓存策略 path('foo/', cache_page(60)(my_view)) ]
局部缓存,灵活性高
使用caches['CACHE配置key']导入具体对象
from django.core.cache import caches cache1 = caches['myalias'] # default默认 cache2 = caches['myalias_2']
相当于直接引入CACHES配置项中的default项
from django.core.cache import cache
缓存api的使用
from django.core.cache import cache
cache.set(key, value, timeout) # 返回值None
cache.get(ket) # 返回值:为key的具体指,如果无返回None
cache.add(key, value) # 只在key不存在时生效 返回:True成功 False失败
cache.get_or_set(key, value, timeout)
cache.set_many(dict, timeout) # dict:key和value的字典
cache.get_many(key_list)
cache.delete(key)
cache.delete_many(key_list)
浏览器缓存策略
优先访问自己的缓存,用户的电脑上的一个存储空间。
强缓存
响应头-Expires
存到什么时候,绝对时间
响应头-Cache-Control
存多长时间,相对时间
协商缓存
针对一些静态文件,大图片强缓存过期后,浏览器先跟服务器商量一下,你的静态文件和音视频有没有变啊,没变的话我接着存,变的话我在更新一下即可
- Last-Modeified响应头和If-Modified-Since请求头 304触发的协商缓存
- Etag响应头和If-None-Match
博客文章缓存方案
cache_page(过期时间s)装饰器
优点:简单
缺点:权限问题,如果超级管理员看来一下文章,所有文章全缓存了,访客到访时可能会在缓存中看到博主的私有文章。
例如我的博客,管理员用户登陆后在侧边栏是显示的管理员用户的信息,包括用户名、头像等,放入缓存。此时如果有另一个访客登陆后,显示的本该是他自己的信息,但是确实管理员的。因为直接在缓存中拿数据,此时存的是管理员的信息。
# 例如
from django.utils.decorators import method_decorator
class Index(View):
@method_decorator(cache_page(60))
def get(self, request, en_us_c=None, en_us_tag=None):
pass
删除缓存成本过高(出现新旧数据不一致)
局部缓存--cache.set/get
优点:灵活、存储成本最优、删除成本低
缺点:代码实现成本较高
案例:
在模型类中,定义classmethod
class Topic: @classmethod def get_topic_list(cls,): if cache: return cache data = cls.objects.filter() # cache in return data
自己写缓存装饰器
创建文章前后把所有的cache清空
# 可传参的装饰器写法 def cache_set(expire): def _cache_set(func): def wrapper(request, *args, **kwargs): # 区分场景 - 只做列表页 # 生成 正确的cache_key [访客访问 和 博主访问] # -----判断是否有缓存,有缓存直接返回 # 执行视图 # 存储缓存 直接用cache对象/set/get # 返回响应 return func(request, *args, **kwargs) return wrapper return _cache_set
我的博客缓存装饰器的实现
from blog import models from django.core.cache import cache # 可传参的装饰器写法 def cache_set(expire): def _cache_set(func): def wrapper(request, *args, **kwargs): # 文章列表 login_user_id = request.session.get('user_id') cache_key = 'admin_cache' if login_user_id: login_user = models.UserInfo.objects.get(id=login_user_id) if not login_user.is_admin: cache_key = 'user_cache' # -----判断是否有缓存,有缓存直接返回 res = cache.get(cache_key) if res: return res # 执行视图 res = func(request, *args, **kwargs) # 存储缓存 直接用cache对象/set/get cache.set(cache_key, res, expire) # 返回响应 return res return wrapper return _cache_set
使用
# 使用 from blog.utils.cache_dec import cache_set from django.utils.decorators import method_decorator class Index(View): @method_decorator(cache_set(60)) def get(self, request, en_us_c=None, en_us_tag=None): pass
发文章时要清空缓存达到立见性
如果不清缓存的话,有人访问了一下主页,然后你在缓存的失效时间内(如果设置1分钟过期缓存,那就是一分钟内)发了一篇文章,然后那个人此时是看不到你的文章的,因为是在缓存中拿的数据。所以没有实时性。当然你如果可以容忍这个时间的话也没问题。不容忍,就在发文章时清空缓存
我在admin.py中通过重写ModelAdmin来实现的,之前写过我懒得写文章的简要desc,就取文章前100个字符作为desc,所以现在我们正好在那里进行一个清空缓存操作就行了。在这里清空,发文章或者更新文章都会走这里,一举两得。
class ArticleForm(forms.ModelForm):
class Meta:
model = models.Article
fields = "__all__"
def clean_desc(self):
desc = self.cleaned_data['desc']
content = self.data['content']
renderer = mistune.Renderer(escape=True, hard_wrap=True)
# renderer = HighlightRenderer()
mk = mistune.Markdown(renderer=renderer)
output = mk(content)
soup = BeautifulSoup(output, 'html.parser')
content = soup.text[:150]
print(desc)
# 发文章时删除对应的缓存key
if cache.get(settings.CUSTOM_CACHE_ADMIN):
cache.delete(settings.CUSTOM_CACHE_ADMIN)
if cache.get(settings.CUSTOM_CACHE_USER):
cache.delete(settings.CUSTOM_CACHE_USER)
if desc:
return desc
else:
desc = content
return desc
@admin.register(models.Article)
class ArticleAdmin(admin.ModelAdmin):
form = ArticleForm