我正在使用SQLAlchemy ORM。我有一个Parent
类和一个Child
类。Parent
和Child
是一对多关系。一个Parent
有很多子节点,甚至可能有20 k-30 k。
我的课程是这样设置的
class Parent(Base):
__tablename__ = "parent"
id = mapped_column(BigInteger, primary_key=True, autoincrement=True)
# associations
children = relationship(
"Child",
back_populates="parent",
uselist=True,
cascade="all, delete-orphan",
primaryjoin='and_(Parent.id == Child.parent_id, Child.deleted_at == None)'
)
class Child(Base):
__tablename__ = "child"
id = mapped_column(BigInteger, primary_key=True, autoincrement=True)
# associations:
parent_id = mapped_column(BigInteger, ForeignKey("parent.id",
ondelete="CASCADE"),
nullable=True)
parent = relationship("Parent", back_populates="children")
字符串
问题是,当我试图在一个Parent和10 k个children之间建立一对多的关系时,当对一个真实的PostgreSQL数据库示例运行时,刷新操作需要2分钟。(数据库在高性能服务器上运行,我的笔记本电脑连接非常好。)
下面是我用于性能测试的代码:
session_maker = sessionmaker(autocommit=False, autoflush=False)
session = session_maker()
parent = Parent()
session.add(parent)
session.flush() # fast
session.commit() # fast
children = []
for i in range(10000):
child = Child()
children.append(child)
session.add_all(children)
session.flush() # fast
session.commit() # fast
parent.children.extend(children)
for child in children:
child.parent = parent
session.add(parent)
session.add_all(children)
session.flush() # super slow
session.commit() # super slow
型
对我来说奇怪的是,我可以在不到一秒钟的时间内创建和刷新10 k个孩子,但是当我需要建立一个简单的一对多关系时,需要2分钟。
我拒绝相信这是正常的。
**编辑1:**我将测试脚本改为这样,现在它快多了:
session_maker = sessionmaker(autocommit=False, autoflush=False)
session = session_maker()
parent = Parent()
session.add(parent)
children = []
for _ in range(n_children):
child = Child()
children.append(child)
session.add_all(children)
assert len(children) == n_children
for child in children:
child.parent = parent
parent.children.extend(children)
session.add(parent)
session.add_all(children)
# FLUSHING and COMMITTING:
start_time = time.time()
session.flush() # fast
session.commit()
print(time.time() - start_time)
for i in range(0, n_children - 1):
assert children[i].id + 1 == children[i + 1].id
assert children[i].parent == parent
assert len(parent.children) == n_children
session.close()
型
正如你所看到的,而不是每次冲水,我只在最后冲水一次。有人能解释一下为什么这会更快吗?这也会在我的代码中导致一些问题,直到刷新ID没有定义。
**编辑2:**当在Parent和Child之间建立一对多关系时,我们正在更新Child表的parent_id列。因此,我们实际上是在尝试执行10 k UPDATE操作。由于某种原因,SQLAlchemy按顺序执行这些操作,因此来回访问数据库10 k次。如果我使用本地数据库,执行时间要快得多(但仍然很慢),大约10秒,这一事实加强了这一理论。是否有一种方法可以并行执行更新?我在看session.bulk_update_mappings
,但执行时间仍然相同。也许有一些发动机的参数
2条答案
按热度按时间drkbr07n1#
在子对象与父对象关联之前持久化子对象时,将行插入到子表中,
parent_id
为NULL。也就是说,字符串
生成
型
然后,当您将它们与父行关联时,SQLAlchemy必须使用实际的parent_id更新所有这些子行。
型
SQLAlchemy创建父行
型
然后调用DBAPI
.executemany()
方法更新子行型
但Wireshark跟踪显示,psycopg2实际上发送给服务器的是每行的单独UPDATE语句
型
即,每个子行一次往返。这对许多儿童来说显然是缓慢的。
相反,将新创建的子对象与父对象 before flushing关联,这将创建父对象,然后使用适当的parent_id创建每个子对象
型
产生
型
baubqpgj2#
它们做同样的事情,但第一个必须为每个孩子执行UPDATE,而第二个不需要这样做。
字符串
这里是echo输出,可能很难阅读,但你可以在那里看到插入和更新。
型