SQLAlchemy: Яка різниця між flush () та commit ()?


422

Яка різниця між flush()і commit()в SQLAlchemy?

Я читав документи, але я не мудріший - вони, здається, передбачають попереднє розуміння, чого я не маю.

Мені особливо цікаво їх вплив на використання пам'яті. Я завантажую деякі дані в базу даних із серії файлів (загалом близько 5 мільйонів рядків), і мій сеанс час від часу перепадає - це велика база даних та машина з не так багато пам’яті.

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

Відповіді:


534

Об'єкт сесії - це в основному поточна транзакція змін у базі даних (оновлення, вставлення, видалення). Ці операції не зберігаються в базі даних до тих пір, поки вони не будуть скоєні (якщо ваша програма з якихось причин перерветься під час транзакції в середині сеансу, будь-які невідомі зміни в межах втрачаються).

Об'єкт сеансу реєструє операції транзакцій з 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')>]

Ще одне: чи знаєте ви, чи збільшує виклик commit () збільшувана пам'ять, або зменшує її?
AP257

2
Це також хибно для db двигунів, які не підтримують транзакції, такі як myisam. Оскільки транзакції не триває, флеш має ще менше, щоб відрізнити себе від фіксації.
недоїдання

1
@underrun Тож якщо я поступлю session.query() після цього session.flush(), чи побачу зміни? Враховуючи, що я використовую MyISAM.
Заморожене полум'я

1
Це хороший чи поганий стиль використання flush()та commit()чи варто це залишити Алхімії. Я використовував flush()в деяких випадках, оскільки для отримання нових даних потрібні наступні запити.
Єнс

1
@Jens Використання autoflush( Trueза замовчуванням). Він автоматично спалахує перед усіма запитами, тому вам не доведеться пам'ятати щоразу.
Kiran Jonnalagadda

24

Як говорить @snapshoe

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

commit() здійснює транзакцію.

Коли session.autocommit == False:

commit()зателефонує, flush()якщо ви встановите autoflush == True.

Коли session.autocommit == True:

Ви не можете зателефонувати, commit()якщо ви не розпочали транзакцію (чого ви, мабуть, не зробили, оскільки ви, ймовірно, використовуватимете лише цей режим, щоб уникнути керування транзакціями вручну).

У цьому режимі потрібно зателефонувати, flush()щоб зберегти зміни ORM. Промивання також ефективно передає ваші дані.


24
"commit () викликає flush (), якщо ваш autoflush == True." не зовсім коректно або просто вводить в оману. Коміт завжди заповнюється, незалежно від налаштування автоматичного змиву.
Ilja Everilä

3
Параметр autoflushконтролює, чи sqlalchemy спочатку видасть флеш, якщо є записи, що очікують на розгляд, перед тим, як надсилати запит, і не має нічого спільного з контролем неминучого флеша при фіксації.
SuperShoot

4

Навіщо рум'янець, якщо ти можеш покластися?

Як хтось, хто починає працювати з базами даних і sqlalchemy, попередні відповіді - що flush()надсилає SQL заяви в БД і commit()зберігає їх - не були мені зрозумілі. Визначення мають сенс, але з визначень не відразу зрозуміло, чому б ви використовували флеш замість того, щоб просто робити.

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

@snapshoe підкреслює, що якщо ви хочете запитувати базу даних і отримувати результати, що включають в себе щойно додані об’єкти, вам потрібно спершу розмититися (або скористатися, що буде для вас спалахнути). Можливо, це корисно для деяких людей, хоча я не впевнений, чому б ви хотіли флексувати, а не вчиняти (крім тривіальної відповіді, що це можна скасувати).

В іншому прикладі я синхронізував документи між локальною БД та віддаленим сервером, і якщо користувач вирішив скасувати, всі додавання / оновлення / видалення повинні бути скасовані (тобто відсутність часткової синхронізації, лише повна синхронізація). Під час оновлення одного документа я вирішив просто видалити стару рядок і додати оновлену версію з віддаленого сервера. Виявляється, через те, як написано sqlalchemy, порядок операцій при вчиненні не гарантується. Це призвело до додавання дубліката версії (перед спробою видалити стару), що призвело до відмови БД унікальним обмеженням. Щоб обійти це, я використав flush()так, щоб порядок підтримувався, але я все одно міг скасувати, якщо пізніше процес синхронізації не вдався.

Дивіться мій пост про це за адресою: Чи є замовлення на додавання до видалення при здійсненні в sqlalchemy

Аналогічно, хтось хотів дізнатися, чи підтримується порядок додавання при здійсненні, тобто якщо я додаю object1потім додаю object2, чи object1додається до бази даних ранішеobject2 SQLAlchemy зберігає замовлення під час додавання об'єктів до сеансу?

Знову ж таки, тут, мабуть, використання флеш () забезпечило б бажану поведінку. Отже, підсумовуючи, одне використання для флеш - це надання гарантій замовлення (я думаю), знову ж таки дозволяючи собі "скасувати" варіант, який зобов'язання не передбачає.

Автоматичне відмивання та автокомісія

Зауважте, автозаправка може бути використана для того, щоб запити діяли на оновленій базі даних, оскільки 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

Іншими словами, коміти повинні зменшити використання пам'яті, хоча, мабуть, тут є компроміс між пам’яттю та продуктивністю. Іншими словами, ви, мабуть, не хочете здійснювати кожну зміну бази даних по одному (з міркувань продуктивності), але занадто довго очікування збільшить використання пам'яті.


1

Це не відповідає чітко на початкове запитання, але деякі люди згадували, що з 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вручну.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.