我有几个问题希望有助于巩固我对GAE“背后的风格”的理解。目前在我的应用程序中,我需要检索一组大小为258个实体的数据。我已将indexed = False设置为此实体的Model类的相应属性。我正在使用Nick Johnson的blog描述的序列化/反序列化实体技术。为了示例目的,我将代码设为通用:
def serialize_entities(models):
if not models:
return None
elif isinstance(models, db.Model):
return db.model_to_protobuf(models).Encode()
else:
return [db.model_to_protobuf(x).Encode() for x in models]
def deserialize_entities(data):
if not data:
return None
elif isinstance(data, str):
return db.model_from_protobuf(entity_pb.EntityProto(data))
else:
return [db.model_from_protobuf(entity_pb.EntityProto(x)) for x in data]
class BatchProcessor(object):
kind = None
filters = []
def get_query(self):
q = self.kind.all()
for prop, value in self.filters:
q.filter("%s" % prop, value)
q.order("__key__")
return q
def run(self, batch_size=100):
q = self.get_query()
entities = q.fetch(batch_size)
while entities:
yield entities
q = self.get_query()
q.filter("__key__ >",entities[-1].key())
entities = q.fetch(batch_size)
class MyKindHandler(webapp2.RequestHandler):
def fill_cache(self, my_cache):
entities = []
if not my_cache:
# retry in case of failure to retrieve data
while not my_cache:
batch_processor = BatchProcessor()
batch_processor.kind = models.MyKind
batch = batch_processor.run()
my_cache = []
for b in batch:
my_cache.extend(b)
# Cache entities for 10 minutes
memcache.set('MyKindEntityCache', serialize_entities(my_cache),
600)
for v in my_cache:
# There is actually around 10 properties for this entity.
entities.append({'one': v.one, 'two': v.two})
mykind_json_str = simplejson.dumps({'entities':entities})
# Don't set expiration - will be refreshed automatically when
# entity cache is.
memcache.set('MyKindJsonCache', mykind_json_str)
return mykind_json_str
def get(self):
my_cache = deserialize_entities(memcache.get('MyKindEntityCache'))
if not my_cache:
# Create entity & json cache
self.fill_cache(my_cache)
mykind_json_str = memcache.get('MyKindJsonCache')
if not mykind_json_str:
mykind_json_str = self.fill_cache(my_cache)
self.response.write(mykind_json_str)
当我查看at appstats时,它会显示检索此数据的处理程序的ajax调用需要花费大量时间。当必须从数据存储区刷新数据时(实体缓存每10分钟到期)调用该处理程序时,会进行434次RPC调用,否则有2个RPC称为检索每个memcaches。刷新数据的appstats是:
real=10295ms cpu=13544ms api=5956ms overhead=137ms (434 RPCs) datastore_v3.Get 428 memcache.Get 2 memcache.Set 2 datastore_v3.RunQuery 2
我想我理解每个调用在大多数情况下都在做什么,但与datastore_v3.RunQuery相关联的datastore_v3.Get调用会让我感到困惑,如果它们代表了我认为他们所做的事情。它们是否意味着那些是对数据存储区的单独.get()调用?我认为当你调用.fetch(batch_size)时,你会收到一个带有1个调用的“批处理”...不仅如此,为什么这些调用的数量比数据存储区中的实体数量多150多个给定的种类?
作为旁注,我在学习查询游标之前编写了BatchProcessor类,并尝试将它们交换出来以查看性能是否变得更好,但它不仅保持相同,而且由于存储的原因而增加了RPC的数量光标在memcache中。
对我可能会忽略的事情的任何见解都非常感谢!
修改 - 来自appstats的更多详细信息:
在对此进行测试并检查appstats时,我发现在创建json响应字符串时,我在每个MyKind上调用.parent()会大大增加RPC,同时每个MyKind有2个ReferenceProperties可变地拥有或者没有价值。我使用此.parent()调用,以便在响应中使用MyKind数据返回公司名称,如果ReferenceProperties具有值,我也会得到它们的名称。我没有意识到调用.parent()。name,.refprop1.name和.refprop2.name会从数据存储区为每个数据存储执行单独的datastore_V3.Get。我认为父和引用属性的数据是与MyKind对象的原始查询一起返回的。有没有办法让这种情况发生,或者我是否需要在MyKind中创建3个其他属性才能有效访问这些名称属性?