SQLAlchemy: flush()和commit()有什么区别?
在SQLAlchemy中,flush()
和commit()
有什么区别呢?
我看过文档,但还是不太明白——文档似乎假设我已经有了一些基础知识,而我并没有。
我特别想知道它们对内存使用的影响。我正在从一系列文件中加载数据到数据库中(总共大约500万行),而我的会话偶尔会崩溃——这是一个大型数据库,而我的机器内存不多。
我在想是不是我使用了太多的commit()
,而flush()
用得不够——但如果不真正理解它们的区别,就很难判断!
8 个回答
当你需要模拟写入的时候,比如想从一个自动增加的计数器中获取主键ID时,就要使用flush。
john=Person(name='John Smith', parent=None)
session.add(john)
session.flush()
son=Person(name='Bill Smith', parent=john.id)
如果不使用flush,john.id
就会是空的。
正如其他人所说,如果不使用 commit()
,这些数据都不会被永久保存到数据库里。
这段话并没有严格回答最初的问题,但有些人提到,当你设置了 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(虽然查询这个对象时会得到ID,这有时会让人困惑,比如“为什么这里可以而那里不行?”不过 snapshoe 已经解释过这个部分了)。
还有一个我觉得很重要但没有提到的方面:
为什么不总是提交? - 答案是 原子性。
这个词的意思是:一组操作要么全部成功执行,要么都不生效。
举个例子,如果你想创建/更新/删除一个对象(A),然后再创建/更新/删除另一个对象(B),但如果(B)失败了,你希望撤销(A)。这就意味着这两个操作是原子的。
因此,如果(B)需要(A)的结果,你希望在(A)之后调用 flush
,在(B)之后调用 commit
。
另外,如果 session.autoflush is True
,除了我上面提到的情况或者 Jimbo 的回答中的其他情况,你就不需要手动调用 flush
了。
会话对象其实就是对数据库进行的一系列操作,比如更新、插入和删除。这些操作在你确认之前不会被保存到数据库里(如果你的程序在这个过程中意外中止,任何未确认的更改都会丢失)。
会话对象通过 session.add()
来记录这些操作,但在你调用 session.flush()
之前,这些操作并不会传递给数据库。
session.flush()
会把一系列操作(插入、更新、删除)传递给数据库。数据库会把这些操作当作待处理的事务来保存。直到数据库收到当前事务的确认(也就是 session.commit()
的作用),这些更改才会被永久保存到硬盘上,其他事务也才能看到这些更改。
session.commit()
就是把这些更改正式保存到数据库里。
flush()
总是会在调用 commit()
的时候被执行。
当你使用会话对象查询数据库时,查询结果会同时包含数据库中的数据和会话对象中已刷新但尚未确认的事务部分的数据。默认情况下,会话对象会自动刷新操作,但你可以选择关闭这个功能。
希望这个例子能让你更清楚:
#---
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')>]