SQLAlchemy: Jaka jest różnica między flush () a commit ()?

422

Jaka jest różnica między flush()i commit()w SQLAlchemy?

Przeczytałem dokumenty, ale nie jestem mądrzejszy - wydaje się, że zakładają wcześniejsze zrozumienie, którego nie mam.

Szczególnie interesuje mnie ich wpływ na wykorzystanie pamięci. Ładuję niektóre dane do bazy danych z serii plików (w sumie około 5 milionów wierszy), a moja sesja czasami się przewraca - jest to duża baza danych i maszyna z małą ilością pamięci.

Zastanawiam się, czy używam zbyt wielu commit()i za mało flush()połączeń - ale tak naprawdę nie rozumiem, na czym polega różnica, trudno powiedzieć!

AP257
źródło

Odpowiedzi:

533

Obiekt sesji jest w zasadzie ciągłą transakcją zmian w bazie danych (aktualizacja, wstawianie, usuwanie). Operacje te nie są utrwalane w bazie danych, dopóki nie zostaną zatwierdzone (jeśli program z jakiegoś powodu przerywa transakcję w trakcie sesji, wszelkie niezatwierdzone zmiany zostają utracone).

Obiekt sesji rejestruje operacje transakcji session.add(), ale jeszcze nie komunikuje ich z bazą danych, dopóki nie session.flush()zostanie wywołana.

session.flush()przesyła szereg operacji do bazy danych (wstawianie, aktualizowanie, usuwanie). Baza danych utrzymuje je jako operacje oczekujące w transakcji. Zmiany nie są trwale zapisywane na dysku ani widoczne dla innych transakcji, dopóki baza danych nie otrzyma instrukcji COMMIT dla bieżącej transakcji (co się session.commit()dzieje).

session.commit() zatwierdza (utrzymuje) te zmiany w bazie danych.

flush()jest zawsze wywoływany jako część połączenia z commit()( 1 ).

Gdy użyjesz obiektu Session do zapytania do bazy danych, zapytanie zwróci wyniki zarówno z bazy danych, jak i z wypłukanych części niezatwierdzonej transakcji, którą przechowuje. Domyślnie Session sprzeciwia się autoflushich działaniu, ale można to wyłączyć.

Mam nadzieję, że ten przykład uczyni to bardziej zrozumiałym:

#---
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')>]
snapshoe
źródło
Jeszcze jedna rzecz: czy wiesz, czy wywołanie commit () zwiększa używaną pamięć, czy ją zmniejsza?
AP257,
2
Dotyczy to również silników db, które nie obsługują transakcji takich jak myisam. Ponieważ nie ma trwającej transakcji, kolor ma jeszcze mniej do odróżnienia od zatwierdzenia.
underrun
1
@underrun Więc jeśli to zrobię session.query() później session.flush(), czy zobaczę moje zmiany? Biorąc pod uwagę, że używam MyISAM.
Frozen Flame
1
Czy jest to dobry czy zły styl w użyciu flush()i commit()czy powinienem pozostawić to Alchemii. Używałem flush()w niektórych przypadkach, ponieważ kolejne zapytania wymagały pobrania nowych danych.
Jens
1
@Jens Use autoflush( Truedomyślnie). Spłuka się automatycznie przed wszystkimi zapytaniami, więc nie musisz za każdym razem pamiętać.
Kiran Jonnalagadda
24

Jak mówi @snapshoe

flush() wysyła instrukcje SQL do bazy danych

commit() zatwierdza transakcję.

Kiedy session.autocommit == False:

commit()zadzwoni, flush()jeśli ustawisz autoflush == True.

Kiedy session.autocommit == True:

Nie możesz zadzwonić, commit()jeśli nie rozpocząłeś transakcji (czego prawdopodobnie nie zrobiłeś, ponieważ prawdopodobnie użyłbyś tego trybu tylko w celu uniknięcia ręcznego zarządzania transakcjami).

W tym trybie musisz zadzwonić, flush()aby zapisać zmiany ORM. Opróżnianie skutecznie zatwierdza również twoje dane.

Jakub
źródło
24
„commit () wywoła flush (), jeśli twój autoflush == True.” nie jest całkowicie poprawne lub wprowadza w błąd. Zatwierdź zawsze opróżnia, niezależnie od ustawienia automatycznego spłukiwania.
Ilja Everilä
3
Parametr autoflushkontroluje, czy sqlalchemy najpierw wyda kolor, jeśli istnieją oczekujące zapisy przed wydaniem zapytania i nie ma nic wspólnego z kontrolowaniem nieuniknionego koloru przy zatwierdzeniu.
SuperShoot,
4

Po co spłukiwać, jeśli możesz popełnić?

Jako ktoś nowy w pracy z bazami danych i sqlalchemy, poprzednie odpowiedzi - które flush()wysyłają instrukcje SQL do bazy danych i commit()nadal je trwają - nie były dla mnie jasne. Definicje mają sens, ale z definicji nie wynika jasno, dlaczego miałbyś używać koloru zamiast po prostu zatwierdzać.

Ponieważ zatwierdzenie zawsze się opróżnia ( https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing ), brzmią bardzo podobnie. Myślę, że główną kwestią do podkreślenia jest to, że kolor nie jest trwały i można go cofnąć, podczas gdy zatwierdzenie jest trwałe, w tym sensie, że nie można poprosić bazy danych o cofnięcie ostatniego zatwierdzenia (myślę)

@snapshoe podkreśla, że ​​jeśli chcesz wykonać zapytanie do bazy danych i uzyskać wyniki, które zawierają nowo dodane obiekty, musisz najpierw wyczyścić (lub zatwierdzić, co będzie dla ciebie opróżniać). Być może jest to przydatne dla niektórych osób, chociaż nie jestem pewien, dlaczego miałbyś chcieć spłukać zamiast zatwierdzić (oprócz trywialnej odpowiedzi, że można to cofnąć).

W innym przykładzie synchronizowałem dokumenty między lokalną bazą danych a zdalnym serwerem i jeśli użytkownik zdecydował się anulować, wszystkie dodania / aktualizacje / usunięcia powinny zostać cofnięte (tzn. Brak częściowej synchronizacji, tylko pełna synchronizacja). Podczas aktualizacji jednego dokumentu postanowiłem po prostu usunąć stary wiersz i dodać zaktualizowaną wersję ze zdalnego serwera. Okazuje się, że ze względu na sposób pisania sqlalchemy kolejność operacji podczas zatwierdzania nie jest gwarantowana. Doprowadziło to do dodania zduplikowanej wersji (przed próbą usunięcia starej), co spowodowało, że baza danych nie wykonała unikalnego ograniczenia. Aby obejść ten problem, użyłem flush(), aby zachować porządek, ale nadal mogłem cofnąć, jeśli później proces synchronizacji się nie powiedzie.

Zobacz mój post na ten temat na stronie: Czy istnieje zamówienie na dodawanie kontra usuwanie podczas zatwierdzania w sqlalchemy

Podobnie, ktoś chciał wiedzieć, czy kolejność dodawania jest zachowywana podczas zatwierdzania, tj. Jeśli dodam, object1a potem dodam object2, czy object1zostanie dodana do bazy danych wcześniej. object2 Czy SQLAlchemy zapisuje kolejność podczas dodawania obiektów do sesji?

Ponownie, tutaj prawdopodobnie użycie flush () zapewniłoby pożądane zachowanie. Podsumowując, jednym z zastosowań flush jest zapewnienie gwarancji zamówień (tak myślę), jednocześnie nadal pozwalając sobie na opcję cofania, której nie zapewnia zatwierdzenie.

Autoflush i Autocommit

Uwaga: można użyć funkcji automatycznego czyszczenia, aby upewnić się, że zapytania działają na zaktualizowanej bazie danych, ponieważ sqlalchemy opróżni się przed wykonaniem zapytania. https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.params.autoflush

Autocommit to coś, czego nie do końca rozumiem, ale wygląda na to, że nie zaleca się jego używania: https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.params. automatyczne zatwierdzanie

Zużycie pamięci

Teraz pierwotne pytanie naprawdę chciało wiedzieć o wpływie koloru do zatwierdzenia dla celów pamięci. Ponieważ zdolność do utrzymywania się lub nie jest czymś, co oferuje baza danych (myślę), zwykłe opróżnianie powinno wystarczyć do odciążenia bazy danych - chociaż popełnienie nie powinno zaszkodzić (faktycznie prawdopodobnie pomaga - patrz poniżej), jeśli nie obchodzi cię cofnięcie .

sqlalchemy używa słabego odwołania do obiektów, które zostały opróżnione: https://docs.sqlalchemy.org/en/13/orm/session_state_management.html#session-referencing-behavior

Oznacza to, że jeśli nie masz obiektu gdzieś wyraźnie trzymanego, np. Na liście lub w dykcie, sqlalchemy nie zachowa go w pamięci.

Masz jednak bazę danych rzeczy do zmartwienia. Przypuszczalnie opróżnienie bez popełnienia wiąże się z pewną karą pamięci, aby utrzymać transakcję. Znów jestem nowy, ale tutaj jest link, który sugeruje dokładnie to: https://stackoverflow.com/a/15305650/764365

Innymi słowy, zatwierdzenia powinny zmniejszyć zużycie pamięci, chociaż przypuszczalnie istnieje tutaj kompromis między pamięcią a wydajnością. Innymi słowy, prawdopodobnie nie chcesz zatwierdzać każdej zmiany bazy danych, pojedynczo (ze względu na wydajność), ale zbyt długie oczekiwanie zwiększy użycie pamięci.

Jimbo
źródło
1

To nie odpowiada ściśle na pierwotne pytanie, ale niektórzy wspominali, że przy session.autoflush = Truetobie nie musisz używać session.flush()... I to nie zawsze jest prawdą.

Jeśli chcesz użyć identyfikatora nowo utworzonego obiektu w środku transakcji , musisz wywołać 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

Wynika to z tego, autoflushże NIE automatycznie wypełnia identyfikator (chociaż zapytanie o obiekt będzie, co czasami może powodować zamieszanie, np. „Dlaczego to działa tutaj, ale nie tam?” Ale snapshoe już obejmuje tę część).


Jeden powiązany aspekt, który wydaje mi się bardzo ważny i nie został tak naprawdę wspomniany:

Dlaczego nie popełniasz cały czas? - Odpowiedzią jest atomowość .

Fantazyjny słowo powiedzieć: to zespół działań muszą wszyscy być wykonana pomyślnie albo żaden z nich nie przyniosą efektu.

Na przykład, jeśli chcesz utworzyć / zaktualizować / usunąć jakiś obiekt (A), a następnie utworzyć / zaktualizować / usunąć inny obiekt (B), ale jeśli (B) się nie powiedzie, chcesz przywrócić (A). Oznacza to, że te 2 operacje są atomowe .

Dlatego jeśli (B) potrzebuje wyniku (A), chcesz wywoływać flushpo (A) i commitpo (B).

Ponadto, jeśli session.autoflush is True, z wyjątkiem przypadku, o którym wspomniałem powyżej lub innych w odpowiedzi Jimbo , nie będziesz musiał dzwonić flushręcznie.

Romain Vincent
źródło