SQLAlchemy: В чем разница между flush () и commit ()?

avatar
AP257
17 ноября 2010 в 04:20
152014
6
521

В чем разница между flush() и commit() в SQLAlchemy?

Я читал документы, но я не стал мудрее - они, кажется, предполагают предварительное понимание, которого у меня нет.

Меня особенно интересует их влияние на использование памяти. Я загружаю некоторые данные в базу данных из серии файлов (всего около 5 миллионов строк), и мой сеанс иногда срывается - это большая база данных и машина с небольшим объемом памяти.

Мне интересно, не слишком ли я использую commit() и недостаточно flush() - но, не понимая, в чем разница, трудно сказать!

Источник

Ответы (6)

avatar
snapshoe
17 ноября 2010 в 06:25
670

Объект сеанса - это, по сути, текущая транзакция изменений в базе данных (обновление, вставка, удаление). Эти операции не сохраняются в базе данных до тех пор, пока они не будут зафиксированы (если ваша программа по какой-либо причине прерывается во время транзакции в середине сеанса, любые незафиксированные изменения в ней теряются).

Объект сеанса регистрирует транзакционные операции с 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')>]
AP257
17 ноября 2010 в 07:07
0

И еще кое-что: знаете ли вы, увеличивает ли использование памяти при вызове commit () или уменьшает ее?

underrun
28 июня 2011 в 20:09
3

Это также неверно для движков db, которые не поддерживают транзакции, такие как myisam. Поскольку текущая транзакция отсутствует, flush еще меньше отличается от фиксации.

Frozen Flame
2 апреля 2015 в 07:54
1

@underrun Итак, если я сделаю session.query() после session.flush(), увижу ли я свои изменения? Учитывая, что я использую MyISAM.

Jens
29 сентября 2015 в 16:26
1

Хорошо или плохо использовать flush() и commit(), или я должен оставить это на усмотрение Алхимии. В некоторых случаях я использовал flush(), потому что последующие запросы требовали получения новых данных.

Kiran Jonnalagadda
17 ноября 2015 в 19:38
0

@FrozenFlame да, поскольку данные видны вам внутри транзакции. Просто еще нет в других сделках.

Kiran Jonnalagadda
17 ноября 2015 в 19:39
1

@Jens Используйте autoflush (по умолчанию True). Он автоматически сбрасывается перед всеми запросами, поэтому вам не нужно каждый раз запоминать.

tnusraddinov
12 марта 2020 в 11:44
0

Вот хорошее объяснение. michaelcho.me/article/…

avatar
Kolya Sotnichenko
22 января 2021 в 10:07
1

commit () записывает эти изменения в базу данных. flush () всегда вызывается как часть вызова commit () (1). Когда вы используете объект Session для запроса базы данных, запрос возвращает результаты как из базы данных, так и из окрашенных в красный цвет частей незарегистрированной транзакции, которую он выполняет.

oskros
22 января 2021 в 10:42
1

Этот ответ на самом деле ничего не добавляет к уже существующим ответам ..?

avatar
Adam Hughes
29 августа 2020 в 18:19
8

Используйте сброс, когда вам нужно имитировать запись, например, чтобы получить идентификатор первичного ключа из счетчика с автоинкрементом.

john=Person(name='John Smith', parent=None)
session.add(john)
session.flush()

son=Person(name='Bill Smith', parent=john.id)

Без сброса john никогда не получит идентификатор из БД и поэтому не сможет представить отношения родитель / потомок в коде.

Как уже говорили другие, без commit() ничего из этого не будет постоянно сохраняться в БД.

avatar
Romain Vincent
23 мая 2020 в 09:42
21

Это не совсем ответ на исходный вопрос, но некоторые люди упомянули, что с session.autoflush = True вам не нужно использовать session.flush() ... И это не всегда так.

Если вы хотите использовать идентификатор вновь созданного объекта в середине транзакции , вы должны позвонить по телефону 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 НЕ автоматически заполняет идентификатор (хотя запрос объекта будет, что иногда может вызвать путаницу, например, «почему это работает здесь, а не там?» Но снапшоу уже покрыл эту часть).


Один связанный аспект, который мне кажется очень важным и на самом деле не упоминался:

Почему бы вам не совершать все время? - Ответ: атомарность .

Замечательное слово: ансамбль операций должен все выполняться успешно, ИЛИ ни одна из них не вступит в силу.

Например, если вы хотите создать / обновить / удалить какой-то объект (A), а затем создать / обновить / удалить другой (B), но если (B) не удалось, вы хотите вернуться (A). Это означает, что эти 2 операции являются атомарными .

Следовательно, если (B) нужен результат (A), вы хотите вызвать flush после (A) и commit после (B).

Кроме того, если session.autoflush is True, за исключением случая, упомянутого выше или других в ответе Джимбо, вам не нужно будет звонить на flush вручную.

avatar
Jimbo
25 марта 2020 в 02:09
17

Зачем сбрасывать, если вы можете зафиксировать?

Как новичок в работе с базами данных и sqlalchemy, предыдущие ответы - что flush() отправляет операторы SQL в БД, а commit() сохраняет их - были мне непонятны. Определения имеют смысл, но из определений не сразу понятно, почему вы должны использовать сброс, а не просто фиксацию.

Поскольку коммит всегда сбрасывается (https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing), это звучит очень похоже. Я думаю, что большая проблема, которую следует выделить, заключается в том, что сброс не является постоянным и может быть отменен, тогда как фиксация является постоянной в том смысле, что вы не можете попросить базу данных отменить последнюю фиксацию (я думаю)

@snapshoe подчеркивает, что если вы хотите запросить базу данных и получить результаты, включающие недавно добавленные объекты, вам необходимо сначала выполнить очистку (или выполнить фиксацию, которая очистится для вас). Возможно, это полезно для некоторых людей, хотя я не уверен, почему вы хотите сбросить, а не зафиксировать (кроме тривиального ответа, что это можно отменить).

В другом примере я синхронизировал документы между локальной базой данных и удаленным сервером, и если пользователь решил отменить, все добавления / обновления / удаления должны быть отменены (т.е. не частичная синхронизация, только полная синхронизация). При обновлении одного документа я решил просто удалить старую строку и добавить обновленную версию с удаленного сервера. Оказывается, из-за того, как написана sqlalchemy, порядок операций при фиксации не гарантируется. Это привело к добавлению дублирующей версии (перед попыткой удаления старой), что привело к сбоям в БД уникального ограничения. Чтобы обойти это, я использовал flush(), чтобы этот порядок поддерживался, но я все равно мог отменить, если позже процесс синхронизации не удался.

См. Мой пост по этому поводу: Есть ли какой-либо порядок добавления или удаления при фиксации в sqlalchemy

Точно так же кто-то хотел знать, сохраняется ли порядок добавления при фиксации, т.е. если я добавляю object1, а затем добавляю object2, object1 добавляется в базу данных до object2 Сохраняет ли SQLAlchemy порядок при добавлении объектов в сеанс?

Опять же, здесь, по-видимому, использование flush () обеспечит желаемое поведение. Таким образом, одним из способов использования промывки является обеспечение гарантии порядка (я думаю), опять же, позволяя себе возможность «отменить», которую коммит не предоставляет.

Автозапуск и автоматическая фиксация

Обратите внимание: автозапуск можно использовать для обеспечения того, чтобы запросы действовали в обновленной базе данных, поскольку sqlalchemy будет сбрасывать данные перед выполнением запроса. https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.params.autoflush

Autocommit - это кое-что еще, что я не совсем понимаю, но похоже, что его использование не рекомендуется: 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 <4> <354>

Это означает, что если у вас нет объекта, явно закрепленного где-то, например, в списке или dict, sqlalchemy не будет хранить его в памяти.

Однако тогда у вас есть проблемы с базой данных. Предположительно, сброс без фиксации приводит к некоторой потере памяти для поддержки транзакции. Опять же, я новичок в этом, но вот ссылка, которая, кажется, предлагает именно это: https://coderhelper.com/a/15305650/764365

Другими словами, коммиты должны уменьшить использование памяти, хотя, по-видимому, здесь есть компромисс между памятью и производительностью. Другими словами, вы, вероятно, не захотите фиксировать каждое изменение базы данных по одному (из соображений производительности), но слишком долгое ожидание приведет к увеличению использования памяти.

Michael Ekoka
2 октября 2020 в 14:56
1

autocommit - это упрощенный и интуитивно понятный способ взаимодействия людей с базами данных. Транзакций нет, или, точнее говоря, каждая операция представляет собой транзакцию, которая начинается и фиксируется автоматически (отсюда autocommit). Подумайте, как можно обновить таблицу через клиент cli, не выполняя операторов begin/commit. Фактически, ваши запросы будут заключены в неявные транзакции. Его не рекомендуется использовать в приложениях, потому что он подрывает один из самых сильных аргументов в пользу СУБД - обещание транзакционной атомарности-согласованности-изоляции-долговечности (ACID).

avatar
Jacob
16 октября 2016 в 17:06
33

Как говорит @snapshoe

flush() отправляет ваши операторы SQL в базу данных

commit() фиксирует транзакцию.

Когда session.autocommit == False:

commit() вызовет flush(), если вы установите autoflush == True.

Когда session.autocommit == True:

Вы не можете позвонить по номеру commit(), если вы не начали транзакцию (чего вы, вероятно, не делали, поскольку вы, вероятно, будете использовать этот режим только для того, чтобы избежать ручного управления транзакциями).

В этом режиме вы должны позвонить по номеру flush(), чтобы сохранить изменения ORM. Флеш также эффективно фиксирует ваши данные.

Ilja Everilä
27 октября 2018 в 18:58
38

"commit () вызовет flush (), если ваш autoflush == True." не совсем правильно или просто вводит в заблуждение. Фиксация всегда сбрасывается, независимо от настройки автозапуска.

SuperShoot
3 сентября 2019 в 10:04
4

Параметр autoflush определяет, будет ли sqlalchemy сначала выполнять сброс, если есть ожидающие записи перед выдачей запроса, и не имеет ничего общего с контролем неизбежного сброса при фиксации.