Python 从数据存储加载数据集并合并到单个字典中。资源问题
我有一个productdatabase,其中包含基于langcodes的每个零件的产品、零件和标签 我现在面临的问题是,需要大量的资源来获取不同的数据集,并将它们合并成一个dict来满足我的需要 数据库中的产品基于特定类型(即颜色、尺寸)的多个零件。每个部分都有一个针对每种语言的标签。我为此创建了4种不同的模型。产品、产品零件、产品零件类型和产品零件标签 我已经把它的范围缩小到了大约10行代码,这些代码可以用来生成问题。到目前为止,我有3种产品、3种类型、每种类型3个部件和2种语言。该请求需要5500毫秒才能生成Python 从数据存储加载数据集并合并到单个字典中。资源问题,python,google-app-engine,Python,Google App Engine,我有一个productdatabase,其中包含基于langcodes的每个零件的产品、零件和标签 我现在面临的问题是,需要大量的资源来获取不同的数据集,并将它们合并成一个dict来满足我的需要 数据库中的产品基于特定类型(即颜色、尺寸)的多个零件。每个部分都有一个针对每种语言的标签。我为此创建了4种不同的模型。产品、产品零件、产品零件类型和产品零件标签 我已经把它的范围缩小到了大约10行代码,这些代码可以用来生成问题。到目前为止,我有3种产品、3种类型、每种类型3个部件和2种语言。该请求需要5
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)中。零件的所有标签也是如此
我之所以需要复杂的dict,是因为我想显示产品的所有数据及其默认部分,并显示可选部分的选择器
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个小时了。也找不出解决办法
…弗雷德里克
编辑:
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毫秒没有memcache
我仍然有点担心3000ms,在本地app_dev服务器上,每次重新加载内存缓存都会被填满。难道不应该把所有的东西都放在里面然后从里面读吗
最后但并非最不重要的一点是,是否有人知道为什么应用程序开发的生产服务器上的请求需要大约10倍的时间
编辑3:
我注意到db.Model的非索引部分被索引了,这会造成不同吗
编辑4:
在查阅AppStats(并理解它之后,花了一些时间。我发现主要问题在于part.type.typeId,其中part.type是db.ReferenceProperty。以前应该见过它。也许解释得更好:)我会重新思考该部分。然后再打给你
…弗雷德里克一些简单的想法:
1) 因为您需要所有结果,而不是像现在这样执行for循环,所以显式地调用fetch()以立即获得所有结果。否则,for循环可能会导致对数据存储的多个查询,因为它一次只能获取如此多的项。例如,您可以尝试:
return ProductParts.gql('WHERE __key__ IN :key', key = self.defaultParts).fetch(1000)
2) 可能在初始请求中只加载部分数据。然后使用AJAX技术根据需要加载附加数据。例如,首先返回产品信息,然后发出额外的AJAX请求以获取部件
3) 正如威尔指出的,查询中的对每个参数执行一次查询
- 问题:IN查询对您提供的每个参数执行一个等于查询的操作。因此,self.defaultParts中的
键实际上执行len(self.defaultParts)
查询
- 可能的改进:尝试更多地去规范化您的数据。具体来说,存储每个零件上使用的产品列表。您可以这样构造零件模型:
您还应该使用,这样您就可以确切地了解为什么您的请求花费了这么长时间。你甚至可以考虑发布关于你的请求和你的帖子的AppStas信息的截图。
如果您重新编写代码,则代码可能是这样的:以较少的数据存储往返次数获取数据(这些更改基于上面的想法1、3和4)
需要注意的一个重要事实是
查询中的(以及!=
查询)会导致在后台生成多个子查询,并且子查询的数量限制为30个
因此,您的ProductParts.gql('WHERE uu-key\uuu-IN:key',key=self.defaultParts)
query实际上会在后台生成len(self.defaultParts)
子查询,如果len(self.defaultParts)
大于30,则会失败
以下是网站的相关部分:
注意:中的和=代码>操作员在幕后使用多个查询。例如,
中的操作符为列表中的每个项目执行单独的底层数据存储查询。返回的实体是所有底层数据存储查询的叉积结果,并且是重复数据消除的。任何单个GQL查询最多允许30个数据存储查询
你可以试着安装你的应用程序,看看还有什么地方它可能会慢下来。我认为问题在于设计:当框架特别讨厌的时候,想要在memcache中构造一个关系连接表
GAE会把你的工作丢掉,因为这需要很长时间,但你一开始就不应该这么做。不幸的是,我自己也是一个同性恋者,所以我不能具体说明应该怎么做。两者都是免费的
class ProductParts(db.Model):
...
products = db.ListProperty(db.Key) # product keys
...
GQL_PROD_PART_BY_KEYS = ProductParts.gql('WHERE __key__ IN :1')
@property
def defaultPartsData(self):
return GQL_PROD_PART_BY_KEYS.bind(self.defaultParts)
GQL_PARTS_BY_PRODUCT = 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 not partData:
for type in typeData:
typeDict[type.typeId] = { 'default' : '', 'optional' : [] }
# here's a new approach that does just ONE datastore query (for each product)
GQL_PARTS_BY_PRODUCT.bind(product)
parts = GQL_PARTS_BY_PRODUCT.fetch(1000)
for part in parts:
if part.key() in self.defaultParts:
part_type = 'default'
else:
part_type = 'optional'
for label in labelsForLangCode:
if label.key() in defaultPart.partLabelList:
typeDict[defaultPart.type.typeId][part_type] = label.partLangLabel
# (end new code)
memcache.add(cache_key, typeDict, 500)
partData = memcache.get(cache_key)
productDict['parts'] = partData
productList.append(productDict)