SQLAlchemy:使用ORM扫描大表?

Blu*_*orn 39 python performance orm sqlalchemy

我目前正在玩SQLAlchemy,这真的很整洁.

为了测试,我创建了一个包含我的图片存档的大表,由SHA1哈希索引(删除重复:-)).这是令人印象深刻的快...

为了好玩,我select *在生成的SQLite数据库上做了相同的操作:

session = Session()
for p in session.query(Picture):
    print(p)
Run Code Online (Sandbox Code Playgroud)

我希望看到哈希滚动,但它只是继续扫描磁盘.与此同时,内存使用率暴涨,几秒后达到1GB.这似乎来自SQLAlchemy的身份地图功能,我认为它只保留弱引用.

有人可以向我解释一下吗?我认为在写出哈希后会收集每个Picture p!?

Blu*_*orn 52

好吧,我刚刚找到了一种方法来做到这一点.将代码更改为

session = Session()
for p in session.query(Picture).yield_per(5):
    print(p)
Run Code Online (Sandbox Code Playgroud)

一次只能加载5张图片.看起来默认情况下查询将一次加载所有行.但是,我还不了解该方法的免责声明.引自SQLAlchemy docs

警告:谨慎使用此方法; 如果多个行中存在相同的实例,则将覆盖最终用户对属性的更改.特别是,通常不可能将此设置与急切加载的集合(即任何lazy = False)一起使用,因为当在后续结果批处理中遇到这些集合时,这些集合将被清除以用于新的加载.

因此,如果在使用ORM时使用yield_per实际上是正确的方法(tm)扫描大量的SQL数据,何时使用它是安全的?

  • 如果每个结果实例有一个结果SQL行,则yield_per是安全的.当您加载或加入一对多关系时,每个实例会获得额外的行.如果您需要有关环境的更多详细信息,可以创建一个关于yield_per的单独问题. (6认同)
  • 经过与MySQL内存泄漏的长期斗争之后,我在yield_per文档中看到了这一点:`另请注意,虽然yield_per()会将stream_results执行选项设置为True,但目前只能通过psycopg2方言来理解这一点,它将使用服务器端游标流式传输结果而不是为此查询预缓冲所有行.其他DBAPI在使所有行可用之前预先缓冲所有行.解决方法:http://stackoverflow.com/a/3699677/281469 (4认同)

zzz*_*eek 33

这是我通常为这种情况做的事情:

def page_query(q):
    offset = 0
    while True:
        r = False
        for elem in q.limit(1000).offset(offset):
           r = True
           yield elem
        offset += 1000
        if not r:
            break

for item in page_query(Session.query(Picture)):
    print item
Run Code Online (Sandbox Code Playgroud)

这避免了DBAPI所做的各种缓冲(例如psycopg2和MySQLdb).如果您的查询具有显式JOIN,它仍然需要被适当地使用,尽管热切加载的集合保证完全加载,因为它们被应用于提供了实际LIMIT/OFFSET的子查询.

我注意到Postgresql几乎和返回大结果集的最后100行一样长,因为它返回整个结果(减去实际的行获取开销),因为OFFSET只是对整个事物进行简单的扫描.

  • 请注意,切片查询将产生相同的效果:`q [offset:offset + 1000]`. (4认同)

Dav*_*ick 7

您可以将图片推迟到仅在访问时检索.您可以在逐个查询的基础上执行此操作.喜欢

session = Session()
for p in session.query(Picture).options(sqlalchemy.orm.defer("picture")):
    print(p)
Run Code Online (Sandbox Code Playgroud)

或者您可以在映射器中执行此操作

mapper(Picture, pictures, properties={
   'picture': deferred(pictures.c.picture)
})
Run Code Online (Sandbox Code Playgroud)

你是怎么做到这里的文档

无论哪种方式执行此操作都将确保仅在访问属性时加载图片.