我正在使用 11 个并行进程执行数据处理任务,每个计算的结果都使用 SQLAlchemy 的 ORM 记录在 MySQL 数据库的 InnoDB 表中。然而,处理时间比预期要长。如果我分析这些并行进程之一的执行情况,我可以看到大约 30% 的时间花费在 InstanceState 类的 expire 方法上,该方法被调用... 292,957,736 次!
计算执行 17,106 次迭代的循环,并且每次迭代执行一次提交。在配置文件中,我看到提交方法被称为 17,868,这似乎处于良好的数量级(761 补充提交可能来自周围代码的其他部分)。但是,我不太清楚该过期方法的作用以及为什么应该多次调用它。是在每次提交时在表的每一行上调用它还是什么?它看起来有点像,因为 if 17,106^2 == 292,615,236... 这种行为正常吗?对于在这种情况下如何做得更好,有什么食谱或建议吗?确切的代码有点复杂[它在 __computeForEvent(...) method of this file 中] 但是,SQLAlchemy 部分在概念上与此等效:
for i in range(17106):
propagations = []
for i in range(19):
propagations.append(Propagation(...))
session.add_all(propagations)
session.commit()
其中 Propagation 是 Base 子类。 任何有关如何加快速度并避免expire(...) 调用爆炸式增长的建议将不胜感激。
最佳答案
对expire()的292M调用表明,当调用commit()时,内存中存在这么多对象,这实际上是一个令人难以置信的巨大数字。
消除这些过期调用的一种直接方法就是转 expire_on_commit为假:
sess = Session(expire_on_commit=False)
解决这个问题的一种更微妙的方法,但这需要更多的小心,就是不保留内存中的所有这些对象,如果我们这样做的话:
for i in range(17106):
session.add_all([Propagation() for i in range(19)])
session.commit()
如果 Propagation() 对象列表在没有引用循环的情况下没有被强引用,假设 cPython 它们将在取消引用时被垃圾收集,并且不会受到 commit() 内过期调用的影响。
还有一种策略可能只是将 commit() 延迟到循环之后,而不是使用lush() 一次处理每组项目。这样,大多数对象将在到达 commit() 时被垃圾回收。
不过,expire_on_commit 仍然是解决此问题的最直接方法。
关于python - 大量调用 SQLAlchemy 的 InstanceState 类的 expire 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20575692/