SQLAlchemy中flush()
和commit()
之间有什么区别?
我已经阅读了文档,但没有更明智 - 他们似乎假设我没有预先理解。
我对它们对内存使用的影响特别感兴趣。我正在从一系列文件(总共约500万行)中将一些数据加载到数据库中,并且我的会话偶尔会崩溃 - 它是一个大型数据库和一台内存不足的机器。
我想知道我是否使用了太多commit()
而没有足够的flush()
来电 - 但如果没有真正了解其中的差异,那就很难说了!
答案 0 :(得分:428)
Session对象基本上是对数据库的更改(更新,插入,删除)的持续事务。这些操作在提交之前不会持久保存到数据库中(如果您的程序在会话中间事务中由于某种原因而中止,则会丢失任何未提交的更改)。
会话对象使用session.add()
注册事务操作,但在调用session.flush()
之前尚未将它们传递给数据库。
session.flush()
将一系列操作传递给数据库(插入,更新,删除)。数据库将它们维护为事务中的挂起操作。在数据库收到当前事务的COMMIT(这是session.commit()
所做的事情)之前,更改不会永久保留到磁盘或对其他事务可见。
session.commit()
将这些更改提交(持久)到数据库。
flush()
总是在调用commit()
(1)时被调用。
当您使用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 :(得分:14)
正如@snapshoe所说
flush()
将您的SQL语句发送到数据库
commit()
提交交易。
当session.autocommit == False时:
如果您的autoflush == True,commit()将调用flush()。
当session.autocommit == True:
时如果您尚未启动事务(您可能没有,因为您可能只使用此模式以避免手动管理事务),则无法调用commit()。
在此模式下,您必须调用flush()以保存ORM更改。有效刷新也会提交您的数据。
答案 2 :(得分:10)
如果可以提交,为什么要刷新?
作为一个刚接触数据库和sqlalchemy的人,以前的答案-flush()
将SQL语句发送到DB,而commit()
保留它们-对我来说还不清楚。这些定义很有意义,但是从定义中并不清楚为什么您要使用冲洗而不是仅仅提交。
由于提交总是刷新(https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing),因此听起来确实很相似。我认为需要强调的主要问题是,刷新不是永久的并且可以撤消,而提交是永久的,在某种意义上,您不能要求数据库撤消上一次提交(我认为)
@snapshoe突出显示,如果要查询数据库并获取包含新添加对象的结果,则需要先刷新(或提交,然后将为您刷新)。也许这对某些人有用,尽管我不确定为什么您要冲洗而不是提交(除了可以撤消的琐碎回答之外)。
在另一个示例中,我正在本地数据库和远程服务器之间同步文档,如果用户决定取消,则应撤消所有添加/更新/删除操作(即,没有部分同步,只有完全同步)。更新单个文档时,我决定仅删除旧行,然后从远程服务器添加更新的版本。事实证明,由于sqlalchemy的编写方式,不能保证提交时的操作顺序。这导致添加了一个重复版本(在尝试删除旧版本之前),这导致数据库无法通过唯一约束。为了解决这个问题,我使用了flush()
来保持顺序,但是如果以后同步过程失败,我仍然可以撤消操作。
有关我的信息,请访问:Is there any order for add versus delete when committing in sqlalchemy
类似地,有人想知道提交时是否保持添加顺序,即如果我先添加object1
然后再添加object2
,object1
是否会在{{1}之前被添加到数据库中}
Does SQLAlchemy save order when adding objects to session?
再次,这里假定使用flush()将确保所需的行为。因此,总而言之,刷新的一种用途是提供顺序保证(我认为),同时又允许自己使用提交不提供的“撤消”选项。
自动刷新并自动提交
请注意,自动刷新可用于确保查询对更新的数据库起作用,因为sqlalchemy将在执行查询之前刷新。 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.autocommit
内存使用情况
现在,最初的问题实际上是想了解刷新与提交对内存的影响。由于持久性或不持久性是数据库提供的东西(我认为),因此仅刷新就足以卸载数据库-尽管如果您不关心撤消操作,提交也不会受到损害(实际上可能会有所帮助-见下文)。 。
sqlalchemy对已刷新的对象使用弱引用:https://docs.sqlalchemy.org/en/13/orm/session_state_management.html#session-referencing-behavior
这意味着,如果您没有将对象明确保留在某个地方(如列表或字典中),则sqlalchemy不会将其保留在内存中。
但是,那么您就需要担心数据库方面的问题。大概刷新而不提交会带来一些内存损失,以维护事务。同样,我对此并不陌生,但是下面的链接似乎恰恰表明了这一点:https://stackoverflow.com/a/15305650/764365
换句话说,提交可能会减少内存使用,尽管这里可能在内存和性能之间进行了权衡。换句话说,您可能不想一次提交每个数据库更改(出于性能原因)一次,但是等待太长时间会增加内存使用量。
答案 3 :(得分:7)
这并不能严格回答最初的问题,但是有些人提到使用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
不会不自动填充ID(尽管查询该对象会,有时会引起混乱,例如“为什么在这里而不是那里?”但是snapshoe已经涵盖了这一部分)。
一个相关方面对我来说似乎很重要,但并未真正提及:
您为什么不一直承诺?-答案是 atomicity 。
一个奇怪的词:必须成功执行所有操作才能完整执行所有操作,否则所有操作都不会生效。
例如,如果要创建/更新/删除某个对象(A),然后创建/更新/删除另一个对象(B),但是如果(B)失败,则要还原(A)。这意味着这两个操作是 atomic (原子的)。
因此,如果(B)需要(A)的结果,则要在(A)之后调用flush
,在(B)之后调用commit
。
如果是session.autoflush is True
,除了我上面提到的情况或Jimbo的答案中的其他情况外,您将不需要手动调用flush
。
答案 4 :(得分:2)
在需要写入时使用冲洗,例如从自动增量计数器获取主键ID。
john=Person(name='John Smith', parent=None)
session.add(john)
session.flush()
son=Person(name='Bill Smith', parent=john.id)
如果不进行刷新,john将永远不会从数据库中获取ID,因此无法在代码中表示父子关系。
就像其他人所说的那样,没有commit()
,这些都不会永久保存到数据库中。
答案 5 :(得分:0)
commit() 在数据库中记录这些变化。 flush() 总是作为commit() (1) 调用的一部分被调用。当您使用 Session 对象查询数据库时,查询会返回来自数据库和它正在执行的未记录事务的红色部分的结果。