NDB 查询超出 GAE 软专用内存限制
NDB Queries Exceeding GAE Soft Private Memory Limit
我目前在 Google App Engine 标准环境中有一个应用程序 运行ning,除其他外,它包含一个大型天气数据数据库和一个生成图表的前端端点这个数据。数据库位于 Google Cloud Datastore 中,Python Flask 应用程序通过 NDB 库访问它。
我的问题如下:当我尝试为超过一周的 WeatherData 生成图表(数据每 5 分钟存储一次)时,我的应用程序超出了 GAE 的软专用内存限制并崩溃。但是,存储在我的每个 WeatherData 实体中的是我想要绘制图表的相关字段,此外还有一个非常大的 json 字符串,其中包含我不需要此绘图应用程序的预测数据。因此,导致我的应用程序超过软私有内存限制的 WeatherData 实体部分甚至不需要在此应用程序中。
因此我的问题如下:有没有办法只查询实体中的某些属性,例如可以在 SQL 样式查询中针对特定列进行查询?同样,我不需要整个预测 json 字符串来作图,只需要存储在实体中的其他几个字段。我尝试 运行 的另一种方法是一次只提取几个实体并将查询拆分为多个 API 调用,但最终花费的时间太长以至于页面超时并且我无法让它正常工作。
下面是我的代码,说明它目前是如何实现和破坏的。非常感谢任何输入:
wDataCsv = 'Time,' + ','.join(wData.keys())
qry = WeatherData.time_ordered_query(ndb.Key('Location', loc),start=start_date,end=end_date)
for acct in qry.fetch():
d = [acct.time.strftime(date_string)]
for attr in wData.keys():
d.append(str(acct.dict_access(attr)))
wData[attr].append([acct.time.strftime(date_string),acct.dict_access(attr)])
wDataCsv += '\n' + ','.join(d)
# Children Entity - log of a weather at parent location
class WeatherData(ndb.Model):
# model for data to save
...
# Function for querying data below a given ancestor between two optional
# times
@classmethod
def time_ordered_query(cls, ancestor_key, start=None, end=None):
return cls.query(cls.time>=start, cls.time<=end,ancestor=ancestor_key).order(-cls.time)
编辑:我尝试了下面答案中 link 中描述的迭代页面获取策略。我的代码已更新为以下内容:
wDataCsv = 'Time,' + ','.join(wData.keys())
qry = WeatherData.time_ordered_query(ndb.Key('Location', loc),start=start_date,end=end_date)
cursor = None
while True:
gc.collect()
fetched, next_cursor, more = qry.fetch_page(FETCHNUM, start_cursor=cursor)
if fetched:
for acct in fetched:
d = [acct.time.strftime(date_string)]
for attr in wData.keys():
d.append(str(acct.dict_access(attr)))
wData[attr].append([acct.time.strftime(date_string),acct.dict_access(attr)])
wDataCsv += '\n' + ','.join(d)
if more and next_cursor:
cursor = next_cursor
else:
break
其中 FETCHNUM
=500。在这种情况下,对于与以前相同长度的查询,我仍然超出了软私有内存限制,而且查询花费的时间要长得多 运行。我怀疑问题可能出在 Python 的垃圾收集器没有删除重新引用的已使用信息,但即使我包含 gc.collect()
我也看不到那里有任何改进。
编辑:
按照下面的建议,我使用投影查询解决了这个问题。我没有为每个自定义查询设置单独的投影,而是每次都简单地 运行 相同的投影:即查询实体的所有属性,不包括 JSON 字符串。虽然这并不理想,因为它每次仍然从数据库中提取无偿信息,但由于必要索引的指数增长,为每个特定查询生成单独的查询是不可扩展的。对于此应用程序,由于每个额外的 属性 都是可以忽略不计的额外内存(除了 json 字符串之外),它有效!
不过您可以使用 projection queries to fetch only the properties of interest from each entity. Watch out for the limitations。而且这仍然不能无限扩展。
您可以将查询拆分为多个请求(更具可扩展性),但使用更大的块,而不仅仅是一对(您一次可以获取 500 个)和游标。查看 How to delete all the entries from google datastore?
中的示例
您可以将您的实例 class 升级到具有更多内存的实例(如果尚未完成)。
您可以提前从大实体准备中间结果(也在数据存储中),并在最后阶段使用这些中间 pre-computed 值。
最后你可以尝试只创建和存储部分图表,最后将它们拼接在一起(只有当它归结为那个时,我不确定它会如何完成,我想它不会是微不足道的)。
我目前在 Google App Engine 标准环境中有一个应用程序 运行ning,除其他外,它包含一个大型天气数据数据库和一个生成图表的前端端点这个数据。数据库位于 Google Cloud Datastore 中,Python Flask 应用程序通过 NDB 库访问它。
我的问题如下:当我尝试为超过一周的 WeatherData 生成图表(数据每 5 分钟存储一次)时,我的应用程序超出了 GAE 的软专用内存限制并崩溃。但是,存储在我的每个 WeatherData 实体中的是我想要绘制图表的相关字段,此外还有一个非常大的 json 字符串,其中包含我不需要此绘图应用程序的预测数据。因此,导致我的应用程序超过软私有内存限制的 WeatherData 实体部分甚至不需要在此应用程序中。
因此我的问题如下:有没有办法只查询实体中的某些属性,例如可以在 SQL 样式查询中针对特定列进行查询?同样,我不需要整个预测 json 字符串来作图,只需要存储在实体中的其他几个字段。我尝试 运行 的另一种方法是一次只提取几个实体并将查询拆分为多个 API 调用,但最终花费的时间太长以至于页面超时并且我无法让它正常工作。
下面是我的代码,说明它目前是如何实现和破坏的。非常感谢任何输入:
wDataCsv = 'Time,' + ','.join(wData.keys())
qry = WeatherData.time_ordered_query(ndb.Key('Location', loc),start=start_date,end=end_date)
for acct in qry.fetch():
d = [acct.time.strftime(date_string)]
for attr in wData.keys():
d.append(str(acct.dict_access(attr)))
wData[attr].append([acct.time.strftime(date_string),acct.dict_access(attr)])
wDataCsv += '\n' + ','.join(d)
# Children Entity - log of a weather at parent location
class WeatherData(ndb.Model):
# model for data to save
...
# Function for querying data below a given ancestor between two optional
# times
@classmethod
def time_ordered_query(cls, ancestor_key, start=None, end=None):
return cls.query(cls.time>=start, cls.time<=end,ancestor=ancestor_key).order(-cls.time)
编辑:我尝试了下面答案中 link 中描述的迭代页面获取策略。我的代码已更新为以下内容:
wDataCsv = 'Time,' + ','.join(wData.keys())
qry = WeatherData.time_ordered_query(ndb.Key('Location', loc),start=start_date,end=end_date)
cursor = None
while True:
gc.collect()
fetched, next_cursor, more = qry.fetch_page(FETCHNUM, start_cursor=cursor)
if fetched:
for acct in fetched:
d = [acct.time.strftime(date_string)]
for attr in wData.keys():
d.append(str(acct.dict_access(attr)))
wData[attr].append([acct.time.strftime(date_string),acct.dict_access(attr)])
wDataCsv += '\n' + ','.join(d)
if more and next_cursor:
cursor = next_cursor
else:
break
其中 FETCHNUM
=500。在这种情况下,对于与以前相同长度的查询,我仍然超出了软私有内存限制,而且查询花费的时间要长得多 运行。我怀疑问题可能出在 Python 的垃圾收集器没有删除重新引用的已使用信息,但即使我包含 gc.collect()
我也看不到那里有任何改进。
编辑:
按照下面的建议,我使用投影查询解决了这个问题。我没有为每个自定义查询设置单独的投影,而是每次都简单地 运行 相同的投影:即查询实体的所有属性,不包括 JSON 字符串。虽然这并不理想,因为它每次仍然从数据库中提取无偿信息,但由于必要索引的指数增长,为每个特定查询生成单独的查询是不可扩展的。对于此应用程序,由于每个额外的 属性 都是可以忽略不计的额外内存(除了 json 字符串之外),它有效!
不过您可以使用 projection queries to fetch only the properties of interest from each entity. Watch out for the limitations。而且这仍然不能无限扩展。
您可以将查询拆分为多个请求(更具可扩展性),但使用更大的块,而不仅仅是一对(您一次可以获取 500 个)和游标。查看 How to delete all the entries from google datastore?
中的示例您可以将您的实例 class 升级到具有更多内存的实例(如果尚未完成)。
您可以提前从大实体准备中间结果(也在数据存储中),并在最后阶段使用这些中间 pre-computed 值。
最后你可以尝试只创建和存储部分图表,最后将它们拼接在一起(只有当它归结为那个时,我不确定它会如何完成,我想它不会是微不足道的)。