У меня есть база данных продуктов, которая содержит продукты, детали и этикетки для каждой детали на основе langcodes.
Проблема, с которой я сталкиваюсь и которой не могу обойтись, заключается в огромном количестве ресурсов, используемых для получения различных наборов данных и объединения их в диктовку в соответствии с моими потребностями.
Продукты в базе данных основаны на ряде частей определенного типа (т. Е. Цвет, размер). И каждая часть имеет ярлык для каждого языка. Для этого я создал 4 разные модели. Продукты, ProductParts, ProductPartTypes и ProductPartLabels.
Я сузил его примерно до 10 строк кода, которые используются для генерации проблемы. На данный момент у меня есть 3 продукта, 3 типа, 3 части для каждого типа и 2 языка. И запрос занимает wooping 5500ms для генерации.
for product in productData:
productDict = {}
typeDict = {}
productDict['productName'] = product.name
cache_key = 'productparts_%s' % (slugify(product.key()))
partData = memcache.get(cache_key)
if not partData:
for type in typeData:
typeDict[type.typeId] = { 'default' : '', 'optional' : [] }
## Start of problem lines ##
for defaultPart in product.defaultPartsData:
for label in labelsForLangCode:
if label.key() in defaultPart.partLabelList:
typeDict[defaultPart.type.typeId]['default'] = label.partLangLabel
for optionalPart in product.optionalPartsData:
for label in labelsForLangCode:
if label.key() in optionalPart.partLabelList:
typeDict[optionalPart.type.typeId]['optional'].append(label.partLangLabel)
## end problem lines ##
memcache.add(cache_key, typeDict, 500)
partData = memcache.get(cache_key)
productDict['parts'] = partData
productList.append(productDict)
Полагаю, проблема заключается в том, что число циклов for слишком велико, и им приходится повторять одни и те же данные снова и снова. labelForLangCode получает все метки из ProductPartLabels, которые соответствуют текущему langCode.
Все детали для продукта хранятся в db.ListProperty (db.key). То же самое касается всех этикеток для детали.
Причина, по которой мне нужен сложный диктат , заключается в том, что я хочу отобразить все данные для продукта с его частями по умолчанию и показать селектор для дополнительного.
Параметры defaultPartsData и optionaPartsData - это свойства в модели продукта, которые выглядят следующим образом:
@property
def defaultPartsData(self):
return ProductParts.gql('WHERE __key__ IN :key', key = self.defaultParts)
@property
def optionalPartsData(self):
return ProductParts.gql('WHERE __key__ IN :key', key = self.optionalParts)
Когда завершенный dict находится в memcache, он работает плавно, но не сбрасывается ли memcache, если приложение переходит в режим гибернации? Также я хотел бы показать страницу для первого пользователя (memcache empty) без огромной задержки.
Также, как я сказал выше, это только небольшое количество деталей / продукта. Каким будет результат, когда будет 30 продуктов из 100 частей.
Является ли одним из решений создание запланированной задачи для кэширования ее в memcache каждый час? Это эффективно?
Я знаю, что это много, но я застрял. Я занимаюсь этим около 12 часов подряд. И не могу найти решение.
.. Фредрик
EDIT:
Снимок экрана AppStats здесь .
Из того, что я могу читать запросы, выглядит нормально в AppStats. только занимает около 200-400 мс. Как разница может быть такой большой?
РЕДАКТИРОВАТЬ 2:
Я реализовал решение dound и добавил abit. Теперь это выглядит так:
langCode = 'en'
typeData = Products.ProductPartTypes.all()
productData = Products.Product.all()
labelsForLangCode = Products.ProductPartLabels.gql('WHERE partLangCode = :langCode', langCode = langCode)
productList = []
label_cache_key = 'productpartslabels_%s' % (slugify(langCode))
labelData = memcache.get(label_cache_key)
if labelData is None:
langDict = {}
for langLabel in labelsForLangCode:
langDict[str(langLabel.key())] = langLabel.partLangLabel
memcache.add(label_cache_key, langDict, 500)
labelData = memcache.get(label_cache_key)
GQL_PARTS_BY_PRODUCT = Products.ProductParts.gql('WHERE products = :1')
for product in productData:
productDict = {}
typeDict = {}
productDict['productName'] = product.name
cache_key = 'productparts_%s' % (slugify(product.key()))
partData = memcache.get(cache_key)
if partData is None:
for type in typeData:
typeDict[type.typeId] = { 'default' : '', 'optional' : [] }
GQL_PARTS_BY_PRODUCT.bind(product)
parts = GQL_PARTS_BY_PRODUCT.fetch(1000)
for part in parts:
for lb in part.partLabelList:
if str(lb) in labelData:
label = labelData[str(lb)]
break
if part.key() in product.defaultParts:
typeDict[part.type.typeId]['default'] = label
elif part.key() in product.optionalParts:
typeDict[part.type.typeId]['optional'].append(label)
memcache.add(cache_key, typeDict, 500)
partData = memcache.get(cache_key)
productDict['parts'] = partData
productList.append(productDict)
Результат намного лучше. Теперь у меня около 3000 мс без memcache и около 700 мс с
Я все еще переживаю за 3000 мс, и на локальном сервере app_dev memcache заполняется при каждой перезагрузке. Разве не надо все туда помещать, а потом читать с него?
И последнее, но не менее важное: кто-нибудь знает, почему на производственном сервере запрос занимает в 10 раз больше времени, чем app_dev?
РЕДАКТИРОВАТЬ 3:
Я заметил, что не из db.Model проиндексированы, может ли это сделать разницу?
РЕДАКТИРОВАТЬ 4:
После консультации с AppStats (и понимания этого потребовалось некоторое время. Похоже, что большие проблемы лежат в part.type.typeId, где part.type - это db.ReferenceProperty. Должен был увидеть это раньше. И, возможно, объяснить это лучше :) Я ' Я переосмыслил эту часть. И вернемся к вам.
.. Фредрик