46

私は現在、SQLAlchemy を少しいじっていますが、これは非常に優れています。

テストのために、SHA1ハッシュでインデックス付けされた写真アーカイブを含む巨大なテーブルを作成しました(重複を削除するため:-))。圧倒的に速かった…

select *楽しみのために、結果の SQLite データベースに対して a と同等のことを行いました。

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

ハッシュがスクロールするのを期待していましたが、代わりにディスクをスキャンし続けました。同時に、メモリ使用量が急増し、数秒後に 1GB に達しました。これは SQLAlchemy の ID マップ機能によるものと思われますが、これは弱参照のみを保持していると私は考えていました。

誰かが私にこれを説明できますか?ハッシュが書き出された後、各Picture pが収集されると思いました!?

4

3 に答える 3

58

さて、私はこれを自分で行う方法を見つけました。コードを次のように変更します

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

一度に 5 つの画像のみを読み込みます。デフォルトでは、クエリは一度にすべての行をロードするようです。ただし、その方法の免責事項はまだわかりません。SQLAlchemy ドキュメントからの引用

警告: この方法は注意して使用してください。同じインスタンスが行の複数のバッチに存在する場合、属性に対するエンド ユーザーの変更は上書きされます。特に、熱心にロードされたコレクション (つまり、lazy=False) でこの設定を使用することは通常不可能です。これらのコレクションは、後続の結果バッチで検出されたときに新しいロードのためにクリアされるためです。

では、ORM の使用中に膨大な量の SQL データをスキャンするのに使用yield_perが実際に正しい方法 (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 が提供されたサブクエリに適用されるため、完全にロードされることが保証されています。

OFFSET は全体を単純にスキャンするだけなので、Postgresql が大きな結果セットの最後の 100 行を返すのに、結果全体 (実際の行フェッチのオーバーヘッドを差し引いたもの) を返すのとほぼ同じくらいの時間がかかることに気付きました。

于 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)
})

その方法は、こちらのドキュメントにあります

どちらの方法でも、属性にアクセスしたときにのみ画像が読み込まれるようになります。

于 2009-07-17T23:06:52.640 に答える