[python] SQLAlchemy : flush ()와 commit ()의 차이점은 무엇입니까?

SQLAlchemy flush()와 차이점은 무엇입니까 commit()?

나는 문서를 읽었지만 더 현명한 사람은 아닙니다-그들은 내가없는 사전 이해를 가정하는 것 같습니다.

특히 메모리 사용에 미치는 영향에 관심이 있습니다. 일련의 파일 (약 5 백만 행)에서 데이터베이스로 일부 데이터를로드하고 있으며 세션이 때때로 넘어갑니다.

전화 를 너무 많이 사용하고 commit()있고 충분하지 않은지 궁금합니다. flush()그러나 차이점을 실제로 이해하지 못하면 말하기가 어렵습니다!



답변

Session 객체는 기본적으로 데이터베이스 변경 (업데이트, 삽입, 삭제)에 대한 지속적인 트랜잭션입니다. 이러한 작업은 커밋 될 때까지 데이터베이스에 유지되지 않습니다 (세션 중간 트랜잭션에서 어떤 이유로 프로그램이 중단 된 경우 커밋되지 않은 변경 내용은 손실 됨).

세션 객체는에 트랜잭션 작업을 등록 session.add()하지만 session.flush()호출 될 때까지 아직 데이터베이스와 통신하지 않습니다 .

session.flush()일련의 작업을 데이터베이스에 전달합니다 (삽입, 업데이트, 삭제). 데이터베이스는 트랜잭션에서 보류중인 작업으로 유지 관리합니다. 데이터베이스가 현재 트랜잭션에 대한 COMMIT를 수신 할 때까지 변경 사항이 디스크에 영구적으로 유지되거나 다른 트랜잭션에 표시 session.commit()되지 않습니다.

session.commit() 이러한 변경 사항을 데이터베이스에 커밋 (지속)합니다.

flush()되어 항상 에 대한 호출의 일환으로 불리는 commit()( 1 ).

Session 개체를 사용하여 데이터베이스를 쿼리하면 쿼리는 데이터베이스와 커밋되지 않은 커밋되지 않은 트랜잭션의 플러시 된 부분 모두에서 결과를 반환합니다. 기본적으로 Session autoflush은 작업을 거부 하지만 비활성화 할 수 있습니다.

이 예제가 다음을 더 명확하게하기를 바랍니다.

#---
s = Session()

s.add(Foo('A')) # The Foo('A') object has been added to the session.
                # It has not been committed to the database yet,
                #   but is returned as part of a query.
print 1, s.query(Foo).all()
s.commit()

#---
s2 = Session()
s2.autoflush = False

s2.add(Foo('B'))
print 2, s2.query(Foo).all() # The Foo('B') object is *not* returned
                             #   as part of this query because it hasn't
                             #   been flushed yet.
s2.flush()                   # Now, Foo('B') is in the same state as
                             #   Foo('A') was above.
print 3, s2.query(Foo).all() 
s2.rollback()                # Foo('B') has not been committed, and rolling
                             #   back the session's transaction removes it
                             #   from the session.
print 4, s2.query(Foo).all()

#---
Output:
1 [<Foo('A')>]
2 [<Foo('A')>]
3 [<Foo('A')>, <Foo('B')>]
4 [<Foo('A')>]


답변

@snapshoe가 말한 것처럼

flush() SQL 문을 데이터베이스로 보냅니다.

commit() 트랜잭션을 커밋합니다.

언제 session.autocommit == False:

commit()flush()설정 하면 전화 합니다 autoflush == True.

언제 session.autocommit == True:

commit()트랜잭션을 시작하지 않은 경우 전화를 걸 수 없습니다 (트랜잭션을 수동으로 관리하는 것을 피하기 위해이 모드 만 사용하지 않았을 수도 있음).

이 모드에서는 flush()ORM 변경 사항을 저장하기 위해 전화해야합니다 . 플러시는 효과적으로 데이터를 커밋합니다.


답변

커밋 할 수 있다면 왜 플러시합니까?

데이터베이스와 sqlalchemy를 처음 사용하는 사람으로서 flush()SQL 문을 DB에 보내고 유지 하는 이전 답변 commit()은 명확하지 않았습니다. 정의는 의미가 있지만 커밋 대신 플러시를 사용하는 이유는 정의에서 즉시 명확하지 않습니다.

커밋은 항상 플러시되므로 ( https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing ) 이러한 소리는 실제로 비슷합니다. 강조해야 할 가장 큰 문제는 플러시가 영구적이지 않고 취소 할 수 있다는 것입니다. 커밋은 영구적이지만 데이터베이스에 마지막 커밋을 실행 취소하도록 요청할 수 없다는 의미에서 영구적입니다 (생각합니다)

@snapshoe는 데이터베이스를 쿼리하고 새로 추가 된 객체가 포함 된 결과를 얻으려면 먼저 플러시 (또는 커밋)해야한다고 강조합니다. 어쩌면 이것은 왜 커밋하기보다는 플러시하기를 원하는지 잘 모르겠지만 (실행 취소 할 수있는 사소한 대답 제외) 일부 사람들에게 유용합니다.

다른 예에서 로컬 DB와 원격 서버간에 문서를 동기화하고 있었고 사용자가 취소하기로 결정한 경우 모든 추가 / 업데이트 / 삭제를 취소해야합니다 (즉, 부분 동기화가 아닌 전체 동기화 만). 단일 문서를 업데이트 할 때 단순히 이전 행을 삭제하고 원격 서버에서 업데이트 된 버전을 추가하기로 결정했습니다. sqlalchemy가 작성되는 방식으로 인해 커밋시 작업 순서가 보장되지는 않습니다. 이로 인해 이전 버전을 삭제하기 전에 중복 버전이 추가되어 DB가 고유 제약 조건에 실패했습니다. 이 문제를 해결 flush()하기 위해 순서를 유지하기 위해 사용 했지만 나중에 동기화 프로세스가 실패하면 여전히 실행 취소 할 수 있습니다.

:에서이 내 게시물을 참조 SQLAlchemy의에 투입 할 때 순서가 삭제 대 추가를 위해 있는가

마찬가지로, 누군가는 추가 주문이 커밋 때, 내가 추가하는 경우, 즉 유지되어 있는지 여부를 알고 싶어 object1다음 추가 object2, 않습니다 object1전에 데이터베이스에 추가됩니다 object2
위해 절약 할 SQLAlchemy의 세션에 객체를 추가 할 때?

여기서도 flush ()를 사용하면 원하는 동작을 보장 할 수 있습니다. 요약하면, 플러시의 한 가지 용도는 주문 보장을 제공하는 것입니다 (제 생각에).하지만 커밋이 제공하지 않는 “실행 취소”옵션을 계속 허용합니다.

자동 플러시 및 자동 커밋

autoflush는 sqlalchemy가 쿼리를 실행하기 전에 플러시 할 때 쿼리가 업데이트 된 데이터베이스에서 작동하도록하는 데 사용될 수 있습니다. https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.params.autoflush

자동 커밋은 내가 완전히 이해하지 못하는 다른 것이지만 사용이 권장되지 않는 것처럼 들립니다
. 자동 커밋

메모리 사용량

이제 원래의 질문은 실제로 메모리 목적으로 플러시 대 커밋의 영향에 대해 알고 싶었습니다. 데이터베이스가 제공하거나 제공하지 않는 기능이기 때문에, 플러싱은 데이터베이스로 오프로드하기에 충분해야합니다. 실행 취소에 신경 쓰지 않는다면 커밋이 손상되지 않아야 (실제로는 도움이 됨-아래 참조) .

sqlalchemy는 플러시 된 객체에 대해 약한 참조를 사용합니다. https://docs.sqlalchemy.org/en/13/orm/session_state_management.html#session-referencing-behavior

이것은리스트 나 dict과 같이 어딘가에 명시 적으로 보유 된 오브젝트가없는 경우, sqlalchemy는 오브젝트를 메모리에 보관하지 않음을 의미합니다.

그러나 데이터베이스 측면에서 걱정할 사항이 있습니다. 아마도 커밋하지 않고 플러시하는 것은 트랜잭션을 유지하기 위해 약간의 메모리 페널티가 있습니다. 다시, 나는 이것에 익숙하지 않지만 여기에 이것을 정확하게 제안하는 것 같습니다 : https : //.com/a/15305650/764365

다시 말해, 커밋은 메모리와 성능 사이에 절충점이있을 수 있지만 메모리 사용량을 줄여야합니다. 다시 말해, 성능상의 이유로 한 번에 하나씩 모든 데이터베이스 변경을 커밋하고 싶지는 않지만 너무 오래 기다리면 메모리 사용량이 증가합니다.


답변

이것은 원래의 질문에 엄격하게 대답하지는 않지만 일부 사람들은 session.autoflush = True당신 과 함께 사용할 필요가 없다고 언급했습니다 session.flush()… 그리고 이것이 항상 사실은 아닙니다.

트랜잭션 중간에 새로 생성 된 개체의 ID를 사용 하려면을 호출해야합니다 session.flush().

# Given a model with at least this id
class AModel(Base):
   id = Column(Integer, primary_key=True)  # autoincrement by default on integer primary key

session.autoflush = True

a = AModel()
session.add(a)
a.id  # None
session.flush()
a.id  # autoincremented integer

때문이다 autoflush않습니다 NOT (개체의 쿼리가 가끔 “여기가 아니라 거기에 왜이 작품?”와 같이 혼란을 일으킬 수 것이다 그러나 비록 자동 ID를 입력 snapshoe 이미이 부분을 포함).


나에게 매우 중요하고 실제로 언급되지 않은 한 가지 관련 측면 :

왜 항상 헌신하지 않겠습니까? 답은 원 자성 입니다.

멋진 말 : 작업의 앙상블이 모두 성공적으로 실행되거나 아무 것도 적용되지 않습니다.

예를 들어, 일부 객체 (A)를 생성 / 업데이트 / 삭제 한 다음 다른 객체 (B)를 생성 / 업데이트 / 삭제하려는 경우 (B)가 실패하면 (A)를 되돌리려 고합니다. 이것은 그 두 가지 작업이 원자 적이라는 것을 의미합니다 .

따라서 (B)에 (A) 결과가 필요한 경우 ( flushA) commit이후 및 (B) 이후 에 전화를 걸려 고 합니다.

또한 session.autoflush is True위에서 언급 한 경우 나 Jimbo 의 답변 에서 다른 경우를 제외하고는 flush수동으로 전화를 걸 필요가 없습니다 .


답변