【问题标题】:NDB Queries Exceeding GAE Soft Private Memory Limit超出 GAE 软私有内存限制的 NDB 查询
【发布时间】:2018-02-23 02:50:05
【问题描述】:

我目前有一个在 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() 我也看不到任何改进。

编辑:

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

【问题讨论】:

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


    【解决方案1】:

    您可以使用projection queries 仅从每个实体中获取感兴趣的属性。不过请注意limitations。而且这仍然不能无限扩展。

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

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

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

    最后,您可以尝试只创建和存储图表的一部分,最后将它们拼接在一起(如果归根结底,我不确定它会如何完成,我想它不会'不重要)。

    【讨论】:

    • 非常感谢!投影查询肯定回答了我的问题。我在这里遇到的唯一问题是我的网页用作复选框,用户可以在其中决定他们想要绘制哪些属性。看到有 21 个复选框,这使得 21 选择 1 + 21 选择 2 + ... + 21 选择 21 种生成图形的方式,并且对于投影查询,必须为这些方式中的每一种指定索引。有没有办法创建灵活的索引,或者以某种方式绕过这个限制?谢谢!
    • 为了解决这个索引问题,我实际上编写了一个快速脚本来生成所有这些组合,但正如预期的那样,搜索这个未排序的索引列表来运行查询会使机器在这种情况下无法使用。我也尝试了迭代方法,正如在原始问题中编辑的那样。
    • 属性需要被索引,但这并不一定意味着您需要为每个属性设置一个不同的复合索引,可能可以解决它。这可能很有趣:stackoverflow.com/questions/48388128/…
    猜你喜欢
    • 2013-02-08
    • 2012-04-24
    • 2012-03-09
    • 2014-10-31
    • 1970-01-01
    • 2018-12-29
    • 2020-04-23
    • 2016-01-04
    相关资源
    最近更新 更多