在SQLAlchemy中flush()和commit()之间有什么区别?
我读了这些文件,但还是不明白——他们似乎假设了一个我没有的预先理解。
我对它们对内存使用的影响特别感兴趣。我正在从一系列文件(总共大约500万行)中加载一些数据到数据库中,我的会话偶尔会崩溃——这是一个很大的数据库,而且机器没有太多内存。
我想知道我是否使用了太多的commit()而没有足够的flush()调用-但如果没有真正理解其中的区别,就很难判断!
在SQLAlchemy中flush()和commit()之间有什么区别?
我读了这些文件,但还是不明白——他们似乎假设了一个我没有的预先理解。
我对它们对内存使用的影响特别感兴趣。我正在从一系列文件(总共大约500万行)中加载一些数据到数据库中,我的会话偶尔会崩溃——这是一个很大的数据库,而且机器没有太多内存。
我想知道我是否使用了太多的commit()而没有足够的flush()调用-但如果没有真正理解其中的区别,就很难判断!
当前回答
除非您了解数据库事务是什么,否则现有的答案没有多大意义。(直到最近,我自己也是这样。)
有时,您可能希望运行多条SQL语句,并让它们作为一个整体成功或失败。例如,如果您希望执行从帐户a到帐户B的银行转账,您将需要执行两个查询,例如
UPDATE accounts SET value = value - 100 WHERE acct = 'A'
UPDATE accounts SET value = value + 100 WHERE acct = 'B'
如果第一个查询成功,但第二个查询失败,这就不好了(原因很明显)。因此,我们需要一种方法来“作为一个整体”来处理这两个查询。解决方案是以BEGIN语句开始,以COMMIT语句或ROLLBACK语句结束,例如
BEGIN
UPDATE accounts SET value = value - 100 WHERE acct = 'A'
UPDATE accounts SET value = value + 100 WHERE acct = 'B'
COMMIT
这是一个单独的事务。
在SQLAlchemy的ORM中,这可能像这样
# BEGIN issued here
acctA = session.query(Account).get(1) # SELECT issued here
acctB = session.query(Account).get(2) # SELECT issued here
acctA.value -= 100
acctB.value += 100
session.commit() # UPDATEs and COMMIT issued here
如果监视各种查询执行的时间,您将看到更新直到调用session.commit()才会到达数据库。
在某些情况下,您可能希望在发出COMMIT之前执行UPDATE语句。(也许数据库向对象发出一个自动递增的id,而您希望在commit之前获取它)。在这些情况下,您可以显式地flush()会话。
# BEGIN issued here
acctA = session.query(Account).get(1) # SELECT issued here
acctB = session.query(Account).get(2) # SELECT issued here
acctA.value -= 100
acctB.value += 100
session.flush() # UPDATEs issued here
session.commit() # COMMIT issued here
其他回答
这并没有严格地回答最初的问题,但有些人已经提到了会话。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中。
简单定位:
Commit做出真正的更改(它们在数据库中可见) 同花顺会产生虚构的更改(仅对您可见)
想象一下数据库像git- branches一样工作。
First you have to understand that during a transaction you are not manipulating the real database data. Instead, you get something like a new branch, and there you play around. If at some point you write the command commit, that means: "merge my data-changes into main DB data". But if you need some future data, that you can get only after commit (ex. insert into a table, and you need the inserted PKID), then you use the flush command, meaning: "calculate me the future PKID, and reserve it for me". Then you can use that PKID value further in you code and be sure that the real data will be as expected. Commit must always come at the end, to merge into main DB data.
Commit()将这些更改记录在数据库中。Flush()总是作为commit()(1)调用的一部分被调用。当您使用Session对象查询数据库时,查询将返回来自数据库和它正在执行的未记录事务的红色部分的结果。
如果你能承诺,为什么要冲?
作为一个使用数据库和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
换句话说,提交应该减少内存使用,尽管这里可能存在内存和性能之间的权衡。换句话说,您可能不想每次提交一个数据库更改(出于性能原因),但是等待太久会增加内存使用。