5

我正在创建一个脚本,它将同步两个数据库。数据库中有一个数据应该存储为树,所以我将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?或者您知道如何减少执行时间的其他方法吗?

提前致谢。

4

2 回答 2

13

首先,不要使用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()
于 2012-10-01T21:05:12.407 回答
1

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

一种方法是手动构建不带django-mptt.

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

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

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

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

编辑

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

于 2012-09-30T14:55:05.217 回答