我目前正在玩SQLAlchemy,这真的非常整洁。
为了测试,我创建了一个包含我的图片存档的大表,由SHA1哈希索引(删除重复:-))。这是令人印象深刻的快......
为了好玩,我在生成的SQLite数据库上做了相同的select *
:
session = Session()
for p in session.query(Picture):
print(p)
我希望看到哈希滚动,但它只是继续扫描磁盘。与此同时,内存使用率暴涨,几秒后达到1GB。这似乎来自SQLAlchemy的身份地图功能,我认为这只是保留弱引用。
有人可以向我解释一下吗?我认为在写出散列后会收集每个Picture p!?
答案 0 :(得分:50)
好的,我刚刚找到了一种方法来做到这一点。将代码更改为
session = Session()
for p in session.query(Picture).yield_per(5):
print(p)
一次只能加载5张图片。看起来默认情况下查询将一次加载所有行。但是,我还不了解该方法的免责声明。引自SQLAlchemy docs
警告:谨慎使用此方法;如果多个行中存在相同的实例,则将覆盖最终用户对属性的更改。 特别是,通常不可能将此设置与急切加载的集合(即任何lazy = False)一起使用,因为在后续结果批处理中遇到这些集合时将被清除以用于新的负载。
因此,如果使用yield_per
实际上是正确的方式(tm)在使用ORM时扫描大量的SQL数据,何时使用它是安全的?
答案 1 :(得分:29)
这是我通常为这种情况所做的事情:
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
这避免了DBAPI所做的各种缓冲(例如psycopg2和MySQLdb)。如果您的查询具有显式JOIN,它仍然需要被适当地使用,尽管热切加载的集合保证完全加载,因为它们被应用于提供了实际LIMIT / OFFSET的子查询。
我注意到Postgresql几乎与返回大结果集的最后100行一样长,因为它返回整个结果(减去实际的行获取开销),因为OFFSET只是对整个进行简单的扫描的事情。
答案 2 :(得分:7)
您可以将图片推迟到仅在访问时检索。您可以在逐个查询的基础上执行此操作。 像
session = Session()
for p in session.query(Picture).options(sqlalchemy.orm.defer("picture")):
print(p)
或者你可以在mapper
中完成mapper(Picture, pictures, properties={
'picture': deferred(pictures.c.picture)
})
如何执行此操作位于文档here
中无论采用哪种方式,都可以确保只在访问属性时才加载图片。