2

ページ化されたクエリを実行して空のコレクションをテストするためのメモリ効率の良い方法を見つけようとしていますが、大規模なデータベースで効率的に実行する方法がわかりません。テーブル レイアウトは、双方向の backref を持つ Association オブジェクトを使用します。ドキュメントと非常によく似ています。

class Association(Base):
    __tablename__ = 'Association'
    assoc_id = Column(Integer, primary_key=True, nullable=False, unique=True)
    member_id = Column(Integer, ForeignKey('Member.id'))
    chunk_id = Column(Integer, ForeignKey('Chunk.id'))
    extra = Column(Text)
    chunk = relationship("Chunk", backref=backref("assoc", lazy="dynamic"))

class Member(Base):
    __tablename__ = 'Member'
    id = Column(Integer, primary_key=True, nullable=False, unique=True)
    assocs = relationship("Association", backref="member", cascade="all, delete", lazy="dynamic")

class Chunk(Base):
    __tablename__ = 'Chunk'
    id = Column(Integer, primary_key=True, nullable=False, unique=True)
    name = Column(Text, unique=True)

メンバーが削除されると、メンバーの関連付けがカスケードされて削除されます。ただし、チャンク オブジェクトはデータベース内で孤立します。孤立したチャンクを削除するには、次のようなクエリを使用して空のコレクションをテストできます。

session.query(Chunk).filter(~Chunk.assoc.any())

次に、チャンクを次のように削除します。

query.delete(synchronize_session=False)

ただし、関連付けテーブルとチャンク テーブルが大きい場合、クエリまたはサブクエリがすべてをロードし、メモリが急上昇するようです。

ここで、ページ化されたクエリを使用して標準クエリのメモリ使用量を制限するという概念を見てきました。

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

for chunk in page_query(Session.query(Chunk)):
    print chunk.name

ただし、メモリ使用量がまだ多いため、これは空のコレクション クエリでは機能しないようです。このような空のコレクションに対してページ クエリを実行する方法はありますか?

4

1 に答える 1

1

ここでいくつか欠けていることがわかりました。空のチャンクのクエリはほとんど問題ないようです。私が見たメモリ使用量の急増は、実際のメンバー自体が削除されたときのコードの数行前のクエリからのものでした。

member = session.query(Member).filter(Member.name == membername).one()
session.delete(member)

ドキュメントによると、セッション(デフォルト)は、セッション/メモリにロードされているオブジェクトのみを削除できます。メンバーが削除されると、カスケードルールに従ってメンバーを削除するために、すべてのアソシエーションが読み込まれます。発生する必要があるのは、パッシブ削除を使用して関連付けの読み込みをバイパスする必要があることです。

追加した:

passive_deletes=True

メンバークラスの関連付け関係と:

ondelete='CASCADE'

Associationクラスのmember_id外部キーに。私はSQLite3を使用しており、ドキュメントごとにエンジン接続イベントで外部キーのサポートを追加しました。

孤立したチャンクに関しては、query.deleteメソッドを使用してチャンクの一括削除を行う代わりに。以下に示すように、オフセットを含まないページクエリを使用し、ループ内のセッションからチャンクを削除しました。これまでのところ、メモリスパイクはないようです。

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

for chunk in page_query(query):
    # Do something with the chunk if needed
    session.delete(chunk)
session.commit()

簡単に言うと、コレクションが大きい親オブジェクトを削除する場合は、passive_deletes=Trueを使用すると非常に役立つようです。ページクエリもこの状況でうまく機能しているように見えますが、チャンクがセッションインラインから削除されていたため、オフセットを削除する必要がありました。

于 2012-12-02T15:47:03.503 に答える