通常在django中查询数据都是直接使用django提供的orm,例如,
Post.objects.get(id=1)
这个操作固然方便,但当数据量大或者是数据库压力变大时就不得不考虑引入缓存了,特别是对于那些大部分时间并不会被修改的对象来说。
一开始的想法很简单,实现一个独立的cache访问文件,其中提供接口已从cache中读取,
# post_cache.py
def get_post(post_id):
key = build_cache_key(Post, post_id)
val = cache.get(key)
if not val is None:
return val
val = Post.objects.get(id=post_id)
cache.set(key, val, EXPIRATION)
return val
# category_cache.py
def get_category(category_id):
...
对于不同的model对象分别提供不同的函数以支持从缓存读取。除了提供get之外还需要注意缓存中数据的更新,缓存数据更新这一步比较适合的方法是采用django的signal机制,通过响应model的post_save来进行数据更新。
def post_save_update_post(sender, instance, **kwargs):
post = instance
key = build_cache_key(Post, post.id)
cache.set(key, post, EXPIRATION)
def post_save_update_category(sender, instance, **kwargs):
...
实现了上述代码后,则可以进行如下调用,
post = post_cache.get_post(post_id=1)
category = category_cache.get_category(category_id=1)
基本的功能需求算是实现了。但仔细观察一下就会发现这种写法太过繁琐,每一个需要缓存的model都要来这么一下定义还是很麻烦的,特别是还要处理post_save。容易遗忘,容易出错。
不过观察上述代码后也不难发现很多地方是相似的,本着dry的编码原则,重复代码是需要整合的,
# model_cache.py
def get(model_class, pk_id):
key = build_cache_key(model_class, pk_id)
val = cache.get(key)
if not val is None:
return val
val = model_class.objects.get(pk=pk_id)
cache.set(key, val, EXPIRATION)
return val
def model_post_save(sender, instance, **kwargs):
key = build_cache_key(sender, instance.pk)
val.set(key, instance, EXPIRATION)
经过简单的抽象合并后,就可以这么进行使用,
post = model_cache.get(Post, 1)
category = model_cache.get(Category, 1)
很明显一致了很多。不过上述实现还是有点太弱,比如只支持用主键查询缓存,想根据别的唯一字段进行查询就不能了,所以需要进一步优化处理,
# model_cache.py
def get(model_class, **kwargs):
key = build_cache_key(model_class, **kwargs)
val = cache.get(key)
if not val is None:
return val
val = model_class.objects.get(**kwargs)
cache.set(key, val, EXPIRATION)
return val
在使用时需要改用命名参数,
post = model_class.get(Post, id=1)
category = model_class.get(Category, id=1)
category = model_class.get(Category, title='foobar')
上述实现还需要考虑build_cache_key函数生成的key的有效性、唯一性。此外post_save的更新函数也需要进行相应修改,就不再列出了。
现在得到的版本相比最初是改观很大了,不过用着还是有些不方便。参看下django orm提供的接口,
Post.objects.get(id=1)
缓存访问是不是也可以采用类似的形式呢?好吧,进一步修改,
class ModelCacheManager(object):
def __init__(self, model_class):
self.model_class = model_class
def get(self, **kwargs):
....
class Post(models):
...
Post.cache = ModelCacheManager(Post)
在将之前访问缓存的get操作等封装导ModelCacheManager中之后,在外层就可以这么进行调用,
post = Post.cache.get(id=1)
category = Category.cache.get(id=1)
经过这么一系列的改进,算是有一个能用版本了。这个版本依然有不足,比如接收的参数也可以改为django orm的那些参数,然后可以考虑进一步实现cache.filter(...)等操作。一句话,经可能简化缓存的访问调用。不过这些进一步的优化就有待需要使用的时候再行添加了。