#google-app-engine #google-cloud-datastore
# #google-app-engine #google-облако-хранилище данных
Вопрос:
У меня есть несколько вопросов, которые, надеюсь, помогут укрепить мое понимание «за сценами» GAE. В настоящее время в моем приложении мне необходимо получить набор данных размером 258 объектов. Я установил indexed=False для соответствующих свойств класса модели этого объекта. Я использую метод сериализации / десериализации сущности, описанный в блоге Ника Джонсона. Я сделал код универсальным для примера:
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 amp; 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 обработчику, извлекающему эти данные, занимает наибольшее количество времени. При вызове этого обработчика выполняется 434 вызова RPC в то время, когда он должен обновлять данные из хранилища данных (каждые 10 минут истекает срок действия кэша объектов), в противном случае существует 2 RPC, вызываемых для извлечения каждого из memcaches. Appstats для обновления данных:
реальный = 10 295 мс процессор = 13544 мс api = 5956 мс накладные расходы = 137 мс (434 RPC) datastore_v3.Получаем 428 memcache.Получаем 2 memcache.Установите 2 datastore_v3.RunQuery 2
Я думаю, я понимаю, что по большей части делает каждый из этих вызовов, но хранилище данных v3.Вызовы Get, связанные с datastore_v3.RunQuery, сбивают меня с толку, если они представляют то, что я думаю, что они делают. Означают ли они, что это отдельные вызовы .get() для хранилища данных? Я подумал, что когда вы звоните.извлеките (batch_size), что вы возвращаете «пакет» с 1 вызовом … и не только это, почему количество этих вызовов должно быть на 150 больше, чем количество объектов в хранилище данных для данного типа?
В качестве примечания, я написал класс BatchProcessor до того, как узнал о курсорах запросов, и попытался поменять их местами, чтобы посмотреть, улучшилась ли производительность, но это не только остается прежним, но и увеличивает количество RPC из-за хранения курсора в memcache.
Любое понимание чего-то, что я, возможно, упускаю из виду, очень ценится!
Редактировать — Более подробная информация из appstats:
При тестировании этого и более тщательном изучении appstats я обнаружил, что мои вызовы .parent() для каждого MyKind при создании строки ответа json значительно увеличивают RPC, наряду с наличием 2 ReferenceProperties для каждого MyKind, которые могут иметь или не иметь значения. Я использую этот вызов .parent() для того, чтобы вернуть название компании в ответе с данными MyKind, и если у ReferenceProperties есть значения, я также получаю их имена. Я не понимал, что вызов .parent().name , .refprop1.name , и .refprop2.name выполняет отдельный datastore_V3.Get из хранилища данных для каждого из них. Я думал, что данные родительского и ссылочного свойства были возвращены вместе с исходным запросом для объекта MyKind. Есть ли способ добиться этого, или мне нужно будет создать 3 других свойства в MyKind, чтобы иметь эффективный доступ к этим свойствам имен?
Комментарии:
1. Я не вижу никаких ошибок в приведенном выше коде. Можете ли вы включить код serialize_entities и deserialize_entities?
2. Не задает вопросов. fetch() выполнить a .проникнуть за кулисы?
3. Нет, Вопрос. функция fetch() всегда должна вызывать RunQuery и ноль или более следующих запросов. Get происходит, когда вы разрешаете ссылочное свойство или иным образом извлекаете один известный ключ.
4. С каждым записанным RPC в appstats связана трассировка стека. Что показывают те, что для звонков get?
5. Я отредактировал свой вопрос с более подробной информацией. Основываясь на упоминании Дрю о ссылочных свойствах и моем сканировании appstats с помощью мелкозубой расчески после комментария Ника, моя правка дает результаты!