近来用django开发不少,对其自带自带的”django.middleware.cache.UpdateCacheMiddleware” 和”django.middleware.cache.FetchFromCacheMiddleware”感觉很不爽,原因有两个:
0,cache的过期控制只能通过超时时间进行,而不能主动通知;
1,全页面的cache粒度太粗。
遂想到django统一的models抽象应该很方便对db的cache进行统一处理,所以就有以下的想法:
目前设计的cache存储的数据结构是按db的行进行cache,cache采用memcached作为存储方式,其数据结构是:
{pointer_key -> model_key}, {model_key -> model}。
point_key:以get方法查询的表名和查询参数为基础构建,例如:’user-{‘username’:’qingran}’或者 ‘user-{‘id’:1}’;
model_key:以数据库的表名和表的primary key为基础构建,例如:’user-1’,’user-2’;
model:就是models class的内容了,在db中就是符合primary key的数据行。
这样的结构能使不同的get参数只要是查询同一个表的同一个行,那么就对应同一个model数据。
cache的命中和过期:
在get的方法,现查{point_key -> model_key},然后查询{model_key -> model}最终得到数据。如果有一步命中失败,就从db取,然后回写cache。
在models进行save和delete方法的时候把{model_key -> model}的对应删除。
但是这样的存储结构目前只能缓存models.Model.objects的get方法请求。而无法对filter方法进行cache。filter cache的难点在于这个查询的结果是一个集合,而集合中的任何一个元素的修改或者新添加一个符合filter查询的数据,都会导致cache失效。
所以说解决filter查询的cache需要解决以下问题:
0,save()和delete()方法中发生时能够快速获知filter的cache是否需要立即更新。
可能”需要手动维护所有filter相关缓存的一个key清单,当你有相关数据发生变动时手动清理相关key。”
另外新增加的符合filter查询要求的元素也会引起cache的失效。
1,因为一个filter查询集合内一个元素的失效就清理整个filter集合可能会cache的更新过于频繁。
啰嗦了一堆,上代码:
===========================================
from django.core.cache import cache
from django.db import models
from django.conf import settings
DOMAIN_CACHE_PREFIX = settings.CACHE_MIDDLEWARE_KEY_PREFIX
CACHE_EXPIRE = settings.CACHE_MIDDLEWARE_SECONDS
def cache_key(model, id):
return (“%s-%s-%s” % (DOMAIN_CACHE_PREFIX, model._meta.db_table,
id)).replace(” “, “”)
class GetCacheManager(models.Manager):
# to reload the get method. cache -> db -> cache
def get(self, *args, **kwargs):
id = repr(kwargs)
# in mc, data are stored in {pointer_key -> model_key},{model_key -> model}
# pointer_key is object.get’s method parameters.
# pointer_key = ‘www-user-{‘username’:’qingran}’ or ‘www-user-{‘id’:1}’
pointer_key = cache_key(self.model, id)
# model_key is “<prefix>-<db tablename>-pk”
# model_key = ‘www-user-1’
model_key = cache.get(pointer_key)
if model_key != None:
model = cache.get(model_key)
if model != None:
return model
# cache MISS, get from db.
model = super(GetCacheManager, self).get(*args, **kwargs)
# write data back to cache from db.
if not model_key:
model_key = cache_key(model, model.pk)
cache.set(pointer_key, model_key, CACHE_EXPIRE)
cache.set(model_key, model, CACHE_EXPIRE)
return model
class ModelWithGetCache(models.Model):
# to reload the save method
def save(self, *args, **kwargs):
# first, delete cache {model_key -> model}
model_key = cache_key(self, self.pk)
cache.delete(model_key)
super(ModelWithGetCache, self).save()
# to reload the delete method
def delete(self, *args, **kwargs):
# first, delete cache {model_key -> model}
model_key = cache_key(self, self.pk)
cache.delete(model_key)
super(ModelWithGetCache, self).delete()
===============================================
在使用的时候定义models需要改从ModelWithGetCache继承,并且指定objects = GetCacheManager()
Sample:
class User(ModelWithGetCache):
objects = GetCacheManager()
…