在SQLAlchemy中flush()和commit()之间有什么区别?
我读了这些文件,但还是不明白——他们似乎假设了一个我没有的预先理解。
我对它们对内存使用的影响特别感兴趣。我正在从一系列文件(总共大约500万行)中加载一些数据到数据库中,我的会话偶尔会崩溃——这是一个很大的数据库,而且机器没有太多内存。
我想知道我是否使用了太多的commit()而没有足够的flush()调用-但如果没有真正理解其中的区别,就很难判断!
在SQLAlchemy中flush()和commit()之间有什么区别?
我读了这些文件,但还是不明白——他们似乎假设了一个我没有的预先理解。
我对它们对内存使用的影响特别感兴趣。我正在从一系列文件(总共大约500万行)中加载一些数据到数据库中,我的会话偶尔会崩溃——这是一个很大的数据库,而且机器没有太多内存。
我想知道我是否使用了太多的commit()而没有足够的flush()调用-但如果没有真正理解其中的区别,就很难判断!
当前回答
Session对象基本上是对数据库进行更改(更新、插入、删除)的持续事务。这些操作在提交之前不会持久化到数据库中(如果您的程序在会话事务中由于某种原因中止,其中任何未提交的更改都将丢失)。
会话对象向session.add()注册事务操作,但直到调用session.flush()才将它们传递给数据库。
Session.flush()向数据库传递一系列操作(插入、更新、删除)。数据库将它们作为事务中的挂起操作来维护。更改不会永久保存到磁盘上,也不会对其他事务可见,直到数据库接收到当前事务的COMMIT(这就是session.commit()所做的)。
Session.commit()将这些更改提交(持久化)到数据库。
Flush()总是作为commit()(1)调用的一部分被调用。
当使用Session对象查询数据库时,查询将返回来自数据库和来自其持有的未提交事务的刷新部分的结果。默认情况下,会话对象自动刷新其操作,但这可以被禁用。
希望这个例子能让你更清楚:
#---
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')>]
其他回答
如果你能承诺,为什么要冲?
作为一个使用数据库和sqlalchemy的新手,之前的答案——flush()将SQL语句发送到DB并commit()持久化它们——对我来说并不清楚。这些定义是有意义的,但从定义中不能立即清楚为什么要使用刷新而不是仅仅提交。
由于提交总是刷新(https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing),这些听起来非常相似。我认为需要强调的一个大问题是刷新不是永久的,可以被撤销,而提交是永久的,在某种意义上,你不能要求数据库撤销上次提交(我认为)。
@snapshoe强调,如果你想查询数据库并获得包含新添加对象的结果,你需要先刷新(或提交,这将为你刷新)。也许这对一些人来说是有用的,尽管我不确定为什么你想要flush而不是commit(除了它可以被撤消的琐碎答案)。
In another example I was syncing documents between a local DB and a remote server, and if the user decided to cancel, all adds/updates/deletes should be undone (i.e. no partial sync, only a full sync). When updating a single document I've decided to simply delete the old row and add the updated version from the remote server. It turns out that due to the way sqlalchemy is written, order of operations when committing is not guaranteed. This resulted in adding a duplicate version (before attempting to delete the old one), which resulted in the DB failing a unique constraint. To get around this I used flush() so that order was maintained, but I could still undo if later the sync process failed.
在sqlalchemy中提交时是否有添加和删除的顺序
类似地,有人想知道提交时是否保持添加顺序,即如果我添加object1,然后添加object2, object1是否在object2之前添加到数据库 SQLAlchemy在向会话添加对象时保存顺序吗?
同样,这里假定使用flush()将确保所需的行为。总之,flush的一个用途是提供顺序保证(我认为),同时仍然允许自己使用commit没有提供的“撤消”选项。
自动刷新和自动提交
注意,autoflush可用于确保查询作用于已更新的数据库,因为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
内存使用情况
原来的问题实际上是想知道flush和commit对内存的影响。由于数据库提供了持久化或不持久化的能力(我认为),简单地刷新应该足以将数据卸载到数据库中——尽管如果您不关心撤消,提交应该不会造成伤害(实际上可能会有所帮助——参见下文)。
Sqlalchemy对已刷新的对象使用弱引用:https://docs.sqlalchemy.org/en/13/orm/session_state_management.html#session-referencing-behavior
这意味着如果您没有显式地保留某个对象,例如在列表或字典中,sqlalchemy将不会将其保存在内存中。
但是,您还需要担心数据库方面的问题。假定在未提交的情况下刷新会带来一些内存损失,以维护事务。再说一次,我对这个不熟悉,但这里有一个链接,似乎正好说明了这一点:https://stackoverflow.com/a/15305650/764365
换句话说,提交应该减少内存使用,尽管这里可能存在内存和性能之间的权衡。换句话说,您可能不想每次提交一个数据库更改(出于性能原因),但是等待太久会增加内存使用。
Session对象基本上是对数据库进行更改(更新、插入、删除)的持续事务。这些操作在提交之前不会持久化到数据库中(如果您的程序在会话事务中由于某种原因中止,其中任何未提交的更改都将丢失)。
会话对象向session.add()注册事务操作,但直到调用session.flush()才将它们传递给数据库。
Session.flush()向数据库传递一系列操作(插入、更新、删除)。数据库将它们作为事务中的挂起操作来维护。更改不会永久保存到磁盘上,也不会对其他事务可见,直到数据库接收到当前事务的COMMIT(这就是session.commit()所做的)。
Session.commit()将这些更改提交(持久化)到数据库。
Flush()总是作为commit()(1)调用的一部分被调用。
当使用Session对象查询数据库时,查询将返回来自数据库和来自其持有的未提交事务的刷新部分的结果。默认情况下,会话对象自动刷新其操作,但这可以被禁用。
希望这个例子能让你更清楚:
#---
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')>]
Commit()将这些更改记录在数据库中。Flush()总是作为commit()(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
这是因为autoflush不会自动填充id(尽管对对象的查询会自动填充,但有时会引起混淆,例如“为什么这个在这里工作,而在那里不工作?”但snapshoe已经覆盖了这部分)。
有一个相关的方面对我来说似乎很重要,但却没有被提及:
你为什么不一直承诺呢?答案是原子性。
说得好听点:一组操作必须全部成功执行,否则它们都不会生效。
例如,如果你想创建/更新/删除某个对象(A),然后创建/更新/删除另一个对象(B),但如果(B)失败,你想恢复(A)。这意味着这两个操作是原子的。
因此,如果(B)需要(a)的结果,则希望在(a)之后调用flush,并在(B)之后提交。
同样,if session。autoflush是True,除了我上面提到的情况或Jimbo回答中的其他情况,您将不需要手动调用flush。
当需要模拟写入时使用flush,例如从自动递增计数器获取主键ID。
john=Person(name='John Smith', parent=None)
session.add(john)
session.flush()
son=Person(name='Bill Smith', parent=john.id)
不用脸红,约翰。Id将为null。
正如其他人所说,如果没有commit(),这些都不会永久地持久化到DB中。