【问题标题】:Using yield with multiple ndb.get_multi_async将 yield 与多个 ndb.get_multi_async 一起使用
【发布时间】:2012-12-25 13:07:47
【问题描述】:

我正在尝试提高来自 appengine 数据存储区的当前查询的效率。目前,我使用的是同步方法:

class Hospital(ndb.Model):
      name = ndb.StringProperty()
      buildings= ndb.KeyProperty(kind=Building,repeated=True)
class Building(ndb.Model):
      name = ndb.StringProperty()
      rooms= ndb.KeyProperty(kind=Room,repeated=True)
class Room(ndb.Model):
      name = ndb.StringProperty()
      beds = ndb.KeyProperty(kind=Bed,repeated=True)
class Bed(ndb.Model):
      name = ndb.StringProperty()
      .....

目前我正在愚蠢地经历:

currhosp = ndb.Key(urlsafe=valid_hosp_key).get()
nbuilds = ndb.get_multi(currhosp.buildings)
for b in nbuilds:
   rms = ndb.get_multi(b.rooms)
   for r in rms:
      bds = ndb.get_multi(r.beds)
      for b in bds:
          do something with b object

我想使用 get_multi_async 将其转换为更快的查询

我的困难在于如何做到这一点? 有什么想法吗?

最好的 乔恩

【问题讨论】:

    标签: python google-app-engine asynchronous app-engine-ndb


    【解决方案1】:

    使用上面给定的结构是可能的,并且已确认您可以使用一组 tasklet 解决此问题。这是迭代方法的显着加速。

    @ndb.tasklet
    def get_bed_info(bed_key):
        bed_info = {}
        bed = yield bed_key.get_async()
        format and store bed information into bed_info
        raise ndb.Return(bed_info)
    
    @nbd.tasklet
    def get_room_info(room_key):
        room_info = {}
        room = yield room_key.get_async()
        beds = yield map(get_bed_info,room.beds)
        store room info in room_info
        room_info["beds"] = beds
        raise ndb.Return(room_info)
    
    @ndb.tasklet
    def get_building_info(build_key):
        build_info = {}
        building = yield build_key.get_async()
        rooms = yield map(get_room_info,building.rooms)
        store building info in build_info
        build_info["rooms"] = rooms
        raise ndb.Return(build_info)
    
    @ndb.toplevel
    def get_hospital_buildings(hospital_object):
        buildings = yield map(get_building_info,hospital_object.buildings)
        raise ndb.Return(buildings)
    

    现在来自具有医院对象 (hosp) 的医院函数的主要调用。

    hosp_info = {}
    buildings = get_hospital_buildings(hospital_obj)
    store hospital info in hosp_info
    hosp_info["buildings"] = buildings
    return hosp_info
    

    给你!它非常高效,可以让计划以尽可能快的方式在 GAE 主干中完成所有信息。

    【讨论】:

    • 您可以(而且应该!)接受您自己的答案。这是很好的因果报应。
    • 首先@GuidovanRossum 请返回应用引擎:)。抱歉,继续提问。为什么你有toplevel 的外部函数?这不仅适用于请求处理程序吗?好奇,因为它仍然会提高 ndb.Return?
    • 没关系。这段代码对我的情况有很大帮助。真心欣赏!
    • 万一有人关心,刚刚从测试中发现。在初始 hospital_object.buildings 键上执行 ndb.multi_get() 比在外部小任务上的异步小任务更快。我很好奇是否提前做好准备,或者使用自动批处理器会更快。好像是前者。
    • 如果你得到它,像自动批处理器一样的 multi_get 更快,但是,这允许你将 tasklet 分布在多个核心上。 multi_get AFAIK 不是那样的,它会使你的核心饱和。但是,在许多情况下,您提供了一个很好的提示!我还认为这为更易于阅读的代码提供了非常小的惩罚,并且这允许分段,因此它可以随着您拥有的医院数量而扩展。如果您要执行许多 async_tasklet,则不推荐使用 Multi_get。很好的是,您没有使用 multi_get 获得的 async_pauses
    【解决方案2】:

    【讨论】:

    • 这使我能够探索包括小任务在内的可能性,因此我能够构建我发布的答案。
    【解决方案3】:

    这是不可能的。 您的第二个查询 (ndb.get_multi(b.rooms)) 取决于您的第一个查询的结果。 所以拉它异步dosnt工作,因为此时第一个查询的(第一个)结果无论如何都必须可用。 NDB 在后台执行类似的操作(在您处理第一个结果时,它已经缓冲了 ndb.get_multi(currhosp.buildings) 的下一项)。 但是,您可以使用非规范化,即保留一个大表,每个 Building-Room-Bed 对有一个条目,然后从该表中提取结果。 如果您对该表的读取次数多于写入次数,这将大大提高您的速度(读取 1 个 DB,而不是 3 个)。

    【讨论】:

    • 有可能 - 请参阅我上面的评论
    • 看来我错了,抱歉。在弄清楚小任务中发生了什么之后,您实际上可以加快速度。但这仅影响整体时间。直到第一个结果可用的时间和 DB-Reads 的数量(=成本)仍然很高。因此,根据您的情况,非规范化仍然可能是更好的解决方案。
    • 是的 - 我同意非规范化会起作用 - 这是我的下一步!关于我应该阅读哪些内容以帮助创建非规范化方案的任何建议?
    猜你喜欢
    • 2012-09-03
    • 1970-01-01
    • 1970-01-01
    • 2012-01-03
    • 2014-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多