在迭代过期查询和更新时了解SQLAlchemy性能

时间:2014-02-13 21:38:07

标签: python mysql sqlalchemy

我正在迭代SQLAlchemy中的查询并对行执行更新,我遇到了意想不到的糟糕性能,比我预期的要慢几个数量级。该数据库有大约12K行。我原来的查询和循环看起来像这样:

query_all = session.query(MasterImages).all()

for record_counter, record in enumerate(query_all):
    # Some stuff happens here, set_id and set_index are defined
    session.query(MasterImages).\
            filter(MasterImages.id == record.id).\
            update({'set_id':set_id, 'set_index':set_index})
    if record_counter % 100 == 0:
        session.commit()
        print 'Updated {:,} records'.format(record_counter)
session.commit()

循环的第一次迭代非常快,但在第一次提交后它似乎会停止。我尝试了一些不同的方法,但没有在哪里。然后我尝试更改我的查询,以便它只选择我需要的字段来计算我在更新中使用的set_idset_index值,如下所示:

query_all = session.query(MasterImages.id, MasterImages.project_id, 
                          MasterImages.visit, MasterImages.orbit, 
                          MasterImages.drz_mode, MasterImages.cr_mode).all()

这产生了我期待的表现,在一分钟之内完成了所有记录。在考虑了一段时间之后,我认为我的问题是在我的第一个查询中,在提交之后,变成了陈旧的查询,因为我已经更新了一个(不必要地)在我正在迭代的查询中的字段。我认为,这迫使Alchemy在每次提交后重新生成查询。通过删除我正在迭代的查询更新的字段我认为我能够使用相同的查询,这导致我的性能提高。

我说错了吗?

1 个答案:

答案 0 :(得分:0)

关闭expire_on_commit

expire_on_commit打开的情况下,SQLAlchemy将query_all中的所有对象标记为.commit()之后的过期(或“stale”,如你所说)。过期的意思是,当您下次尝试访问对象上的属性时,SQLAlchemy将发出SELECT来刷新对象。关闭expire_on_commit将阻止它执行此操作。 expire_on_commit是一个选项,因此ORM的天真用法不会被破坏,所以如果你知道你正在做什么,你可以安全地将其关闭。

您的修复工作正常,因为当您在查询()中指定列(MasterImages.id等)而不是映射类(MasterImages)时,SQLAlchemy会返回一个普通的python元组而不是映射类的实例。元组不提供ORM功能,即它不会过期,它永远不会从数据库中重新获取。