46

我目前正在玩一些 SQLAlchemy,这真的很整洁。

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

select *为了好玩,我对生成的 SQLite 数据库做了相当于 a的操作:

session = Session()
for p in session.query(Picture):
    print(p)

我希望看到哈希滚动,但它只是继续扫描磁盘。与此同时,内存使用量猛增,几秒钟后就达到了 1GB。这似乎来自 SQLAlchemy 的身份映射功能,我认为它只是保留弱引用。

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

4

3 回答 3

58

好的,我只是自己找到了一种方法。将代码更改为

session = Session()
for p in session.query(Picture).yield_per(5):
    print(p)

一次只加载 5 张图片。默认情况下,查询似乎一次加载所有行。但是,我还不明白该方法的免责声明。引用SQLAlchemy 文档

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

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

于 2009-07-17T22:23:39.357 回答
37

对于这种情况,我通常会这样做:

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 行所需的时间几乎与返回整个结果所需的时间一样长(减去实际的行获取开销),因为 OF​​FSET 只是对整个事物进行简单扫描。

于 2009-08-02T01:28:45.793 回答
9

您可以将图片推迟到仅在访问时检索。您可以逐个查询地执行此操作。喜欢

session = Session()
for p in session.query(Picture).options(sqlalchemy.orm.defer("picture")):
    print(p)

或者你可以在映射器中完成

mapper(Picture, pictures, properties={
   'picture': deferred(pictures.c.picture)
})

你是怎么做的在文档here

无论哪种方式都可以确保仅在您访问该属性时才加载图片。

于 2009-07-17T23:06:52.640 回答