AP2*_*257 386 python sqlalchemy
SQLAlchemy flush()和commit()SQLAlchemy 之间有什么区别?
我已经阅读了文档,但没有更明智 - 他们似乎假设我没有预先理解.
我对它们对内存使用的影响特别感兴趣.我正在从一系列文件(总共约500万行)中将一些数据加载到数据库中,并且我的会话偶尔会崩溃 - 它是一个大型数据库和一台内存不足的机器.
我想知道我是否使用了太多但commit()没有足够的flush()电话 - 但是如果没有真正了解其中的差异,那就很难说了!
sna*_*hoe 473
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')>]
Run Code Online (Sandbox Code Playgroud)
Jac*_*cob 20
正如@snapshoe所说
flush()将SQL语句发送到数据库
commit()提交交易.
session.autocommit == False时:
如果你的autoflush == True,commit()将调用flush().
当session.autocommit == True时:
如果您尚未启动事务(您可能没有,因为您可能只使用此模式来避免手动管理事务),则无法调用commit().
在此模式下,您必须调用flush()以保存ORM更改.有效刷新也会提交您的数据.
Rom*_*ent 18
这并没有严格回答最初的问题,但有些人提到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
Run Code Online (Sandbox Code Playgroud)
这是因为autoflush它不自动填写ID(尽管对象的查询将,这有时会导致混乱,如“为什么这个作品在这里,但不是吗?”但是,snapshoe已涵盖这一部分)。
一个对我来说似乎很重要但并未真正提及的相关方面:
你为什么不一直承诺?- 答案是原子性。
一个花哨的说法:一组操作必须全部成功执行,否则它们都不会生效。
例如,如果您想创建/更新/删除某个对象 (A),然后创建/更新/删除另一个 (B),但如果 (B) 失败,您想恢复 (A)。这意味着这两个操作是原子的。
因此,如果 (B) 需要 (A) 的结果,您需要flush在 (A)commit之后和(B) 之后调用。
此外,如果session.autoflush is True,除了我上面提到的情况或金博回答中的其他情况外,您将不需要flush手动拨打电话。
Jim*_*mbo 16
如果可以提交,为什么要刷新?
作为使用数据库和 sqlalchemy 的新手,之前的答案 -flush()将 SQL 语句发送到数据库并commit()保留它们 - 我不清楚。这些定义是有道理的,但从定义中并不能立即清楚为什么要使用刷新而不是仅仅提交。
由于提交总是刷新(https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing),这些听起来非常相似。我认为要强调的大问题是刷新不是永久性的并且可以撤消,而提交是永久性的,从某种意义上说,您不能要求数据库撤消最后一次提交(我认为)
@snapshoe 强调,如果您想查询数据库并获得包含新添加对象的结果,您需要先刷新(或提交,它将为您刷新)。也许这对某些人有用,尽管我不确定您为什么要刷新而不是提交(除了可以撤消的简单答案)。
在另一个例子中,我在本地数据库和远程服务器之间同步文档,如果用户决定取消,所有添加/更新/删除都应该被撤消(即没有部分同步,只有完全同步)。更新单个文档时,我决定简单地删除旧行并从远程服务器添加更新版本。事实证明,由于 sqlalchemy 的编写方式,无法保证提交时的操作顺序。这导致添加了重复版本(在尝试删除旧版本之前),从而导致数据库无法通过唯一约束。为了解决这个问题,我使用了flush()这个顺序,但如果稍后同步过程失败,我仍然可以撤消。
请参阅我的帖子:在 sqlalchemy 中提交时,是否有添加与删除的顺序
同样,有人想知道提交时是否保持添加顺序,即如果我添加object1然后添加object2,是否object1会在将对象添加到会话时 SQLAlchemy 保存顺序之前添加到数据库中object2
?
同样,这里大概使用 flush() 将确保所需的行为。因此,总而言之,flush 的一个用途是提供顺序保证(我认为),同时仍然允许自己使用 commit 不提供的“撤消”选项。
自动刷新和自动提交
请注意,自动刷新可用于确保查询对更新的数据库起作用,因为 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。自动提交
内存使用情况
现在最初的问题实际上想知道出于内存目的的刷新与提交的影响。由于持久化的能力是数据库提供的(我认为),简单的刷新应该足以卸载到数据库 - 尽管提交不应该受到伤害(实际上可能有帮助 - 见下文),如果你不关心撤消.
sqlalchemy 对已刷新的对象使用弱引用:https : //docs.sqlalchemy.org/en/13/orm/session_state_management.html#session-referencing-behavior
这意味着如果您没有将对象显式保存在某处,例如在列表或字典中,sqlalchemy 不会将其保存在内存中。
但是,那么您需要担心数据库方面的事情。据推测,在不提交的情况下刷新会带来一些内存损失来维护事务。再次,我是新手,但这里有一个链接似乎暗示了这一点:https : //stackoverflow.com/a/15305650/764365
换句话说,提交应该减少内存使用,尽管这里可能需要在内存和性能之间进行权衡。换句话说,您可能不想一次提交每个数据库更改(出于性能原因),但等待时间过长会增加内存使用量。
Ben*_*Ben 12
除非您了解数据库事务是什么,否则现有答案没有多大意义。(直到最近我自己的情况也是如此。)
有时您可能希望运行多个 SQL 语句并让它们作为一个整体成功或失败。例如,如果您想执行从账户 A 到账户 B 的银行转账,您需要执行两个查询,例如
UPDATE accounts SET value = value - 100 WHERE acct = 'A'
UPDATE accounts SET value = value + 100 WHERE acct = 'B'
Run Code Online (Sandbox Code Playgroud)
如果第一个查询成功但第二个查询失败,这很糟糕(出于明显的原因)。因此,我们需要一种方法来将这两个查询“视为一个整体”。解决方案是以 BEGIN 语句开始,以 COMMIT 语句或 ROLLBACK 语句结束,例如
BEGIN
UPDATE accounts SET value = value - 100 WHERE acct = 'A'
UPDATE accounts SET value = value + 100 WHERE acct = 'B'
COMMIT
Run Code Online (Sandbox Code Playgroud)
这是单笔交易。
在 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
Run Code Online (Sandbox Code Playgroud)
如果您监视各种查询的执行时间,您将看到更新不会到达数据库,直到您调用session.commit().
在某些情况下,您可能希望在发出 COMMIT 之前执行 UPDATE 语句。(也许数据库向对象发出一个自动递增的 id,并且您希望在提交之前获取它)。在这些情况下,您可以显式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
Run Code Online (Sandbox Code Playgroud)
当您需要模拟写入时使用刷新,例如从自动递增计数器获取主键 ID。
john=Person(name='John Smith', parent=None)
session.add(john)
session.flush()
son=Person(name='Bill Smith', parent=john.id)
Run Code Online (Sandbox Code Playgroud)
如果不刷新,john将永远不会从数据库中获取 ID,因此无法在代码中表示父/子关系。
就像其他人所说的那样,如果commit()没有,这些都不会永久保存到数据库中。
对于简单的定向:
commit进行真正的更改(它们在数据库中变得可见)flush进行虚构的更改(它们仅对您可见)想象一下数据库的工作方式就像 git 分支:
transaction 您并没有操作真正的数据库数据。branch,然后你就可以玩了。commit,则意味着:“merge我的数据更改为主数据库数据”。commit(例如,插入到表中,并且您需要插入PKID),那么您可以使用该flush命令,意思是:“计算我的未来PKID,并为我保留它”。然后,您可以PKID在代码中进一步使用该值,并确保真实数据符合预期。Commit必须始终位于最后,以合并到主数据库数据中。| 归档时间: |
|
| 查看次数: |
94404 次 |
| 最近记录: |