【发布时间】:2011-11-09 10:37:45
【问题描述】:
我一直致力于创建一个自动缓存的 db.Model 子类,即:
- instance.put 会将实体存储在内存缓存中,然后再将其持久化到数据存储中
- class.get_by_key_name 会先检查缓存,如果错过了,会去datastore取回,取回后缓存
我开发了以下方法(这似乎对我有用),但我有几个问题:
- 我已经阅读了 Nick Johnson 在efficient model memcaching 上的文章,该文章建议通过协议缓冲区实现 memcache 的序列化。查看SDK中的memcache API源码,谷歌好像已经默认实现了protobuf序列化。我的解释正确吗?
- 在继承 db.Model 或覆盖这两种方法的过程中,我是否遗漏了一些重要的细节(这可能会在未来得到我)?
- 有没有更有效的方法来实施我在下面所做的事情?
- 是否有指导方针、基准或最佳实践来说明此类实体缓存何时从性能角度有意义?或者缓存实体总是有意义吗?在相关说明中,我是否应该阅读 Google 没有在建模 API 中提供缓存模型的事实?是否有太多特殊情况需要考虑?
以下是我当前的实现。我非常感谢有关缓存实体的任何和所有指导/建议(即使您的回答不是对上述 4 个问题之一的直接回答,而是与整个主题相关)。
from google.appengine.ext import db
from google.appengine.api import memcache
import os
import logging
class CachedModel(db.Model):
'''Subclass of db.Model that automatically caches entities for put and
attempts to load from cache for get_by_key_name
'''
@classmethod
def get_by_key_name(cls, key_names, parent=None, **kwargs):
cache = memcache.Client()
# Ensure that every new deployment of the application results in a cache miss
# by including the application version ID in the namespace of the cache entry
namespace = os.environ['CURRENT_VERSION_ID'] + '_' + cls.__name__
if not isinstance(key_names, list):
key_names = [key_names]
entities = cache.get_multi(key_names, namespace=namespace)
if entities:
logging.info('%s (namespace=%s) retrieved from memcache' % (str(entities.keys()), namespace))
missing_key_names = list(set(key_names) - set(entities.keys()))
# For keys missed in memcahce, attempt to retrieve entities from datastore
if missing_key_names:
missing_entities = super(CachedModel, cls).get_by_key_name(missing_key_names, parent, **kwargs)
missing_mapping = zip(missing_key_names, missing_entities)
# Determine entities that exist in datastore and store them to memcache
entities_to_cache = dict()
for key_name, entity in missing_mapping:
if entity:
entities_to_cache[key_name] = entity
if entities_to_cache:
logging.info('%s (namespace=%s) cached by get_by_key_name' % (str(entities_to_cache.keys()), namespace))
cache.set_multi(entities_to_cache, namespace=namespace)
non_existent = set(missing_key_names) - set(entities_to_cache.keys())
if non_existent:
logging.info('%s (namespace=%s) missing from cache and datastore' % (str(non_existent), namespace))
# Combine entities retrieved from cache and entities retrieved from datastore
entities.update(missing_mapping)
if len(key_names) == 1:
return entities[key_names[0]]
else:
return [entities[key_name] for key_name in key_names]
def put(self, **kwargs):
cache = memcache.Client()
namespace = os.environ['CURRENT_VERSION_ID'] + '_' + self.__class__.__name__
cache.set(self.key().name(), self, namespace=namespace)
logging.info('%s (namespace=%s) cached by put' % (self.key().name(), namespace))
return super(CachedModel, self).put(**kwargs)
【问题讨论】:
-
嘿,我写了一个simple module,它可以满足您的需求。你可以去看看。
-
@Can - 感谢您将我指向您的模型。我实际上对使用 memcache API 执行此操作很感兴趣,因为我认为这是一种处理缓存的更具可扩展性的方式。