如何优化在`django-mptt`中添加新节点?

Posted

技术标签:

【中文标题】如何优化在`django-mptt`中添加新节点?【英文标题】:How optimize adding new nodes in `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 时一起应用所有更改?或者您知道如何减少执行时间的其他方法吗?

提前致谢。

【问题讨论】:

【参考方案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 结构中,然后找出该结构上的更改。但是,如果您的树由于内存限制而非常大,那将不可行。

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

编辑

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

【讨论】:

如何只使用一个update 语句修改整个分支?

以上是关于如何优化在`django-mptt`中添加新节点?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 React 中添加新元素(新节点)?

为啥升级我的版本 django-mptt 后出现此数据库迁移错误?

如何向xml中添加新节点

如何在JaguarDB集群中添加更多节点?

通过 PHP 在 XML 文件中添加新节点

如何在 Pytorch 中为优化器动态添加新参数?