【问题标题】:How optimize adding new nodes in `django-mptt`?如何优化在`django-mptt`中添加新节点?
【发布时间】:2012-09-21 14:33:17
【问题描述】:

我正在创建一个脚本,它将同步两个数据库。数据库中有一个数据应该存储为树,因此我将django-mptt 用于新数据库。当我同步数据库时,我从旧数据库中选择新数据并将其保存在新数据库中。

我想知道是否有更好的方法可以将新节点添加到树中?现在它看起来下一个方式:

...
# Add new data to DB
for new_record in new_records:
    # Find appropriate parent using data in 'new_record'
    parent = get_parent(new_record)

    # Create object which should be added using data in 'new_record'
    new_node = MyMPTTModel(...)
    new_node.insert_at(parent, save = True)
    # Similar to:
    # new_node.insert_at(parent, save = False)
    # new_node.save()

但它的工作速度很慢。我认为它是以这种方式工作的,因为在每次调用 insert_at(..., save = True) 方法后,django-mptt 应该将新节点写入数据库并修改数据库中已经存在的记录的 leftright 键。

有什么方法可以让django-mptt 在我每次调用insert_at 时修改查询,然后在我调用save 时一起应用所有更改?或者您知道如何减少执行时间的其他方法吗?

提前致谢。

【问题讨论】:

    标签: python database django performance django-mptt


    【解决方案1】:

    首先,不要使用insert_at。这不是性能缓慢的原因,但它是不必要的并且看起来很难看。只需设置node.parent:

    for new_record in new_records:
        new_node = MyMPTTModel(..., parent=get_parent(new_record))
        new_node.save()
    

    现在是性能问题。如果您使用的是最新的 mptt(git master,而不是 0.5.4),则有一个名为 delay_mptt_updates 的上下文管理器可以防止 mptt 在您添加所有节点之前进行大量此类更新:

    with transaction.atomic():
        with MyMPTTModel.objects.delay_mptt_updates():
            for new_record in new_records:
                new_node = MyMPTTModel(..., parent=get_parent(new_record))
                new_node.save()
    

    或者,如果您几乎要接触整棵树,您可以使用disable_mptt_updates 来加快速度并在最后重建整棵树:

    with transaction.atomic():
        with MyMPTTModel.objects.disable_mptt_updates():
            for new_record in new_records:
                new_node = MyMPTTModel(..., parent=get_parent(new_record))
                new_node.save()
        MyMPTTModel.objects.rebuild()
    

    【讨论】:

    • @craigds - 在用新的父节点更新 现有 节点时这是否有效?我已经尝试在最近的 MPTT 中使用这两个上下文管理器,但即使在重建后它似乎也不起作用。
    【解决方案2】:

    Django-MPTT 为您维护一个树形结构。因此,在每个 insert_at 处,它都会修改插入节点右侧的所有节点 - 这就是您遇到性能问题的原因。

    一种方法是手动构建不带django-mptt的树形结构。

    因此,您将必须获取新记录,并根据它们确定树中的旧节点必须修改多少。由于您只是插入数据,因此只有左右属性会更改,但不会更改级别,因此应该会更容易一些。一旦您知道要修改哪些节点,您就可以使用一个update 事务来修改它们(edit)。

    然后,您可以开始插入新数据。同样,最快的方法是计算每个新条目的左、右和级别值,然后执行一次bulk_insert (Django>=1.4)。这样做只会导致两个数据库操作,这在数据库事务方面显然应该快得多。

    然而,这种方法需要一些聪明的方法来弄清楚如何更改树中的旧节点。最简单的方法是将所有树转储到 python 结构中,然后找出该结构上的更改。但是,如果您的树由于内存限制而非常大,那将不可行。

    现在不确定是否有更有效的方法来做到这一点。也许 StackOverflow 上的其他人有一些很酷的想法......

    编辑

    对于update 的混淆,我们深表歉意。我的意思是一笔交易。在这种情况下,我通常会在 update tbname set ... where id=1; update tbname set ... where id=2; 处执行原始 sql 查询,所以我在一个 sql 查询中执行多个 sql 语句。根据我的经验,数据库的昂贵部分不是执行语句,而是事务本身,因为存在网络延迟、数据库锁等。因此,拥有一个事务可以让数据库尽可能快。但是不确定如何在 django 中使用查询集来做到这一点。我通常做原始的 sql 查询。

    【讨论】:

    • 如何只使用一个update 语句修改整个分支?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-07
    • 2011-08-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多