NDB查询超过GAE软私有内存限制

时间:2018-02-23 02:50:06

标签: google-app-engine google-cloud-datastore app-engine-ndb

我目前有一个在Google App Engine标准环境中运行的应用程序,其中包含一个大型天气数据数据库和一个生成此数据图表的前端端点。该数据库位于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(),我也看不到任何改进。

编辑:

根据以下建议,我使用Projection Queries修复了问题。我没有为每个自定义查询单独投影,而是每次都运行相同的投影:即查询除JSON字符串之外的实体的所有属性。虽然这并不理想,因为它仍然每次从数据库中提取无偿信息,但由于必要指数的指数增长,生成每个特定查询的单个查询不可扩展。对于这个应用程序,因为每个额外的属性可以忽略不计的额外内存(除了形成json字符串),它的工作原理!

1 个答案:

答案 0 :(得分:2)

您可以使用projection queries仅从每个实体获取感兴趣的属性。但请注意limitations。而这仍无法无限扩大。

您可以跨多个请求(更具可扩展性)拆分查询,但使用更大的块,而不仅仅是一对(您可以一次获取500个)和游标。查看How to delete all the entries from google datastore?

中的示例

您可以将实例类转换为具有更多内存的实例(如果尚未完成)。

您可以提前从大型实体准备中间结果(也在数据存储区中),并在最后阶段使用这些中间预先计算的值。

最后,您可以尝试创建和存储图形的一部分,最后将它们拼接在一起(只有当它归结为那时,我不确定它将如何完成,我想它不会'是微不足道的。)