SQLAlchemy:flush()とcommit()の違いは何ですか?


422

違いは、間に何であるflush()commit()SQLAlchemyの中に?

私はドキュメントを読みましたが、賢明ではありません-彼らは私が持っていないことを事前に理解しているようです。

特にメモリ使用量への影響に興味があります。一連のファイル(合計約500万行)から一部のデータをデータベースに読み込んでいますが、セッションがときどきフォールオーバーします。これは、大規模なデータベースであり、メモリの少ないマシンです。

呼び出しが多すぎてcommit()足りないのでflush()はないかと思いますが、違いが何であるかを本当に理解していなければ、それを伝えるのは困難です。

回答:


534

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')>]

もう1つ、commit()を呼び出すと、使用するメモリが増えるか、減らすかがわかりますか?
AP257、2010年

2
これは、myisamなどのトランザクションをサポートしていないdbエンジンの場合もfalseです。進行中のトランザクションがないので、フラッシュはコミットとそれ自体を区別することがさらに少なくなります。
アンダーラン

1
@underrunだから私がsession.query() 後にした場合session.flush()、私の変更が表示されますか?MyISAMを使用していると仮定します。
Frozen Flame

1
flush()and を使用するのは良いスタイルか悪いスタイルかcommit()、それともAlchemyに任せるべきか。flush()後続のクエリで新しいデータを取得する必要があるため、場合によっては使用しました。
Jens

1
@Jens Use autoflushTrueデフォルト)。すべてのクエリの前に自動的にフラッシュされるため、毎回覚えておく必要はありません。
キランジョナラガッダ2015年

24

@snapshoeが言うように

flush() SQLステートメントをデータベースに送信する

commit() トランザクションをコミットします。

いつsession.autocommit == False

commit()flush()設定しautoflush == Trueた場合に呼び出されます。

いつsession.autocommit == True

commit()トランザクションを開始していない場合は呼び出すことができません(手動でトランザクションを管理することを避けるためにこのモードを使用するだけなので、おそらく開始していません)。

このモードでは、呼び出しflush()てORMの変更を保存する必要があります。フラッシュはまた、データを効果的にコミットします。


24
「autoflush == Trueの場合、commit()はflush()を呼び出します。」完全に正しくない、または誤解を招くだけです。自動フラッシュの設定に関係なく、コミットは常にフラッシュされます。
IljaEverilä、2018年

3
autoflushクエリを発行し、コミット時に避けられないフラッシュを制御するとは何の関係もありません前に、保留中の書き込みがある場合はSQLAlchemyのは最初のフラッシュを発行するかどうかのparamを制御します。
SuperShoot

4

コミットできるならなぜフラッシュするのか?

データベースとsqlalchemyを扱うのが初めての人として、flush()SQLステートメントをDBに送信しcommit()て永続化するという以前の答えは、私にはわかりませんでした。定義は理にかなっていますが、コミットするだけでなくフラッシュを使用する理由が定義からすぐにはわかりません。

コミットは常にフラッシュするため(https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing)、これらの音は本当に似ています。強調すべき大きな問題は、フラッシュが永続的ではなく、元に戻すことができることですが、コミットは永続的であるという意味で、最後のコミットを元に戻すようデータベースに要求することはできません(私は思う)

@snapshoeは、データベースにクエリを実行して、新しく追加されたオブジェクトを含む結果を取得する場合は、最初にフラッシュする(またはコミットしてフラッシュする)必要があることを強調しています。おそらくコミットではなくフラッシュする必要がある理由はわかりませんが(これは元に戻すことができるというささいな答えを除いて)、これは一部の人にとっては便利です。

別の例では、ローカルDBとリモートサーバー間でドキュメントを同期していて、ユーザーがキャンセルすることを決定した場合、すべての追加/更新/削除を元に戻す必要があります(つまり、部分同期ではなく、完全同期のみ)。1つのドキュメントを更新するときは、古い行を削除して、リモートサーバーから更新されたバージョンを追加することにしました。sqlalchemyの記述方法により、コミット時の操作の順序は保証されないことがわかりました。これにより、(古いバージョンを削除する前に)重複したバージョンが追加され、DBで一意の制約が失敗しました。これを回避flush()するために、順序が維持されるように使用しましたが、後で同期プロセスが失敗した場合でも元に戻すことができました。

これに関する私の投稿を参照してください:sqlalchemyでコミットするときに追加と削除の順序はありますか?

同様に、誰かがコミット時に追加順序が維持されるかどうかを知りたがっています。つまり、追加してobject1からaddを実行するとobject2SQLAlchemyがオブジェクトをセッションに追加するときに順序を保存するobject1前にデータベースに追加されますobject2 か?

繰り返しますが、ここではおそらくflush()を使用することで目的の動作が保証されます。つまり、要約すると、フラッシュの1つの使用法は、順序の保証を提供することです(私はそう思います)一方で、コミットでは提供されない「取り消し」オプションを自分自身に許可します。

自動フラッシュと自動コミット

sqlalchemyはクエリを実行する前にフラッシュするため、autoflushを使用して、更新されたデータベースでクエリが確実に動作するようにできます。https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.params.autoflush

自動コミットは私には完全には理解されていない何かですが、その使用は推奨されないようです:https : //docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.params。自動コミット

メモリ使用量

現在、元の質問では、メモリの目的でのフラッシュとコミットの影響について知りたがっていました。永続化するかしないかはデータベースが提供するものです(私は思う)、データベースにオフロードするには単純にフラッシュするだけで十分です-元に戻すことを気にしないのであれば、コミットしても害はありません(実際にはおそらく役立ちます-以下を参照)。 。

sqlalchemyは、フラッシュされたオブジェクトに弱い参照を使用します。https//docs.sqlalchemy.org/en/13/orm/session_state_management.html#session-referencing-behavior

これは、リストや辞書のように、どこかに明示的に保持されているオブジェクトがない場合、sqlalchemyはそれをメモリに保持しないことを意味します。

ただし、データベースの側面について心配する必要があります。おそらくコミットせずにフラッシュすると、トランザクションを維持するためのメモリがいくらか犠牲になります。繰り返しますが、これは初めてですが、これはまさにこれを示唆しているように見えるリンクです:https : //stackoverflow.com/a/15305650/764365

つまり、ここではメモリとパフォーマンスの間にトレードオフがあると思われますが、コミットによりメモリ使用量が削減されます。言い換えると、データベースの変更を1つずつコミットするのは望ましくないでしょう(パフォーマンス上の理由から)。ただし、待機時間が長すぎると、メモリ使用量が増加します。


1

これは元の質問に厳密に答えるものではありませんが、一部の人々は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

これはあるautoflushNOT(オブジェクトのクエリが時々 「ここにはありませんが、なぜこの作品?」のような混乱を引き起こす可能性があり、なるしかしものの、自動IDを埋めるsnapshoeすでにこの部分をカバー)。


私にとって非常に重要であると思われ、実際には言及されなかった1つの関連する側面:

なぜあなたはいつもコミットしないのですか?-答えは原子性です。

一言で言うと、一連の操作はすべて正常に実行する必要があります。そうしないと、いずれの操作も有効になりません。

たとえば、あるオブジェクトを作成/更新/削除したい場合(A)、次に別のオブジェクトを作成/更新/削除したい場合(B)、(B)が失敗した場合は元に戻します(A)。つまり、これらの2つの操作はアトミックです。

(B)は(A)の結果を必要とする場合したがって、あなたが呼び出したいflush(A)の後およびcommit(B)の後に。

また、Jimboの回答session.autoflush is Trueで上記または他の人が言及した場合を除いて、手動で呼び出す必要はありません。flush

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.