2012-11-28 2 views
2

빈 콜렉션을 테스트하기 위해 페이징 된 쿼리를 수행하는 메모리 효율적인 방법을 찾고 있지만 대형 데이터베이스에서 효율적으로 수행하는 방법을 파악할 수 없습니다. 테이블 레이아웃은 양방향 백 디렉토리와 연관 개체를 사용합니다. documentation과 매우 유사합니다.SQLAlchemy에서 빈 컬렉션으로 페이지 된 쿼리를 수행하는 방법은 무엇입니까?

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) 

회원이 삭제되면 회원 연계가 계단식으로 삭제됩니다. 그러나 청크 개체는 데이터베이스에서 고아가됩니다. 협회 및 청크 테이블이 큰 경우,

query.delete(synchronize_session=False) 

그러나 다음과 함께 덩어리를 삭제 한 후

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

과 : 고아 청크를 삭제하려면,이 같은 쿼리를 사용하여 빈 콜렉션을 테스트 할 수 있습니다 그것은 쿼리 또는 하위 쿼리가 모든 것을로드하고 메모리가 급증한 것처럼 보입니다. 그러나이 메모리 사용량이 그대로 빈 쿼리 모음 작업을 나타나지 않습니다

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 

:

나는 표준 쿼리 here의 메모리 사용을 제한하는 페이징 쿼리를 사용의 개념을 보았다 여전히 높습니다. 이 같은 빈 컬렉션에 대한 페이징 된 쿼리를 수행 할 수있는 방법이 있습니까?

답변

1

나는 여기에서 놓친 몇 가지를 알아 냈습니다. 빈 청크에 대한 쿼리는 대부분 OK 일 수 있습니다. 필자가 보았던 메모리 사용량은 실제 구성원 자체가 삭제되었을 때 코드에서 몇 줄 앞부분의 쿼리를 통해 나타났습니다.

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

설명서에 따르면 세션 (기본적으로)은 세션/메모리에로드 된 개체 만 삭제할 수 있습니다. 멤버가 삭제되면 캐스케이드 규칙에 따라 멤버를 삭제하기 위해 해당 멤버를 모두로드합니다. 무슨 일이 일어날 필요가 연결로드는 passive deletes을 사용하여 우회했다.

나는 추가 :

passive_deletes=True 
회원 클래스의 연관 관계에

과 :

ondelete='CASCADE' 

을 협회 클래스의 MEMBER_ID 외래 키에. SQLite3을 사용하고 docs에 따라 엔진 연결 이벤트와 함께 외래 키 지원을 추가했습니다.

고아 청크와 관련하여 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를 = 사용에 크게 도움이 보였다. 페이지 쿼리는이 상황에서 청크가 세션 인라인에서 제거되었으므로 오프셋을 제거해야한다는 점에서만 잘 작동하는 것으로 보입니다.

관련 문제