如何优化在`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
应该将新节点写入数据库并修改数据库中已经存在的记录的 left
和 right
键。
有什么方法可以让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`中添加新节点?的主要内容,如果未能解决你的问题,请参考以下文章