SQLAlchemy: flush () ve commit () arasındaki fark nedir?


422

SQLAlchemy arasındaki flush()ve commit()arasındaki fark nedir ?

Dokümanları okudum, ama hiçbiri akıllı değilim - sahip olmadığım bir ön anlayışı var gibi görünüyorlar.

Özellikle bellek kullanımı üzerindeki etkileriyle ilgileniyorum. Bir dizi dosyadan (toplamda yaklaşık 5 milyon satır) bazı verileri yüklüyorum ve oturumum zaman zaman düşüyor - büyük bir veritabanı ve fazla belleği olmayan bir makine.

Çok fazla commit()ve yeterli flush()arama kullanıp kullanmadığımı merak ediyorum - ama farkın ne olduğunu gerçekten anlamadan söylemek zor!

Yanıtlar:


534

Bir Oturum nesnesi temelde bir veritabanında yapılan değişikliklerin (güncelleme, ekleme, silme) devam eden bir işlemidir. Bu işlemler gerçekleştirilinceye kadar veritabanında kalıcı değildir (programınız oturum ortasında işlemde herhangi bir nedenle iptal edilirse, içindeki taahhüt edilmemiş değişiklikler kaybolur).

Oturum nesnesi işlem işlemlerini ile kaydeder session.add(), ancak çağrılıncaya kadar bunları veritabanına iletmez session.flush().

session.flush()bir dizi işlemi veritabanına iletir (ekleme, güncelleme, silme). Veritabanı, bunları bir işlemde bekleyen işlemler olarak tutar. Değişiklikler kalıcı olarak diskte kalmaz veya veritabanı geçerli işlem için bir COMMIT alana kadar (bu da ne session.commit()olur) diğer işlemlerde görünür olmaz.

session.commit() veritabanında bu değişiklikleri yapar (devam eder).

flush()her zamancommit() ( 1 ) çağrısının bir parçası olarak çağrılır .

Veritabanını sorgulamak için bir Oturum nesnesi kullandığınızda, sorgu sonuçları hem veritabanından hem de sahip olduğu taahhüt edilmemiş işlemin boşaltılan bölümlerinden döndürür. Varsayılan olarak, Oturum autoflushişlemlerini reddeder, ancak bu devre dışı bırakılabilir.

Umarım bu örnek bunu daha net hale getirir:

#---
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')>]

Sadece bir şey daha var: commit () çağrısının kullanılan belleği artırdığını veya azalttığını biliyor musunuz?
AP257

2
Bu, myisam gibi işlemleri desteklemeyen db motorları için de yanlıştır. Devam eden bir işlem olmadığından, floş kendini taahhütten ayırmak için daha da azdır.
boşalması

1
@underrun Peki session.query() sonra yaparsam session.flush(), değişikliklerimi görecek miyim? Verilen MyISAM kullanıyorum.
Donmuş Alev

1
Kullanmak iyi mi yoksa kötü bir tarz mı flush()ve commit()bunu Simya'ya bırakmalı mıyım? flush()Bazı durumlarda kullandım çünkü yeni veriler almak için sonraki sorgulara ihtiyaç vardı.
Jens

1
@Jens Kullanımı autoflush( Truevarsayılan olarak). Tüm sorgulardan önce otomatik olarak temizlenir, böylece her seferinde hatırlamanız gerekmez.
Kiran Jonnalagadda

24

@Snapshoe'ın dediği gibi

flush() SQL ifadelerinizi veritabanına gönderir

commit() işlemi gerçekleştirir.

Ne zaman session.autocommit == False:

commit()flush()ayarladıysanız arayacaktır autoflush == True.

Ne zaman session.autocommit == True:

Sen diyemezsin commit()sen (muhtemelen sadece elle yönetecek işlemleri önlemek için bu modu kullanmak istiyorsunuz çünkü muhtemelen değil var) bir işlem başlatılmamış varsa.

Bu modda, flush()ORM değişikliklerinizi kaydetmek için aramalısınız . Yıkama, verilerinizi etkili bir şekilde yerine getirir.


24
"commit (), autoflush == True ise flush () öğesini çağırır." tamamen doğru değil veya sadece yanıltıcı. Otomatik yıkama ayarından bağımsız olarak her zaman yıkama işlemi tamamlanır.
Ilja Everilä

3
autoflushBir sorgu veren ve taahhüt üzerine kaçınılmaz floş kontrol ile ilgisi yoktur derdest yazıyor varsa sqlalchemy ilk floş verip vermemeyi, param denetler.
SuperShoot

4

Taahhüt edebiliyorsanız neden yıkayın?

Veritabanları ve sqlalchemy ile çalışmaya yeni başlayan biri flush()olarak, DB'ye SQL ifadeleri gönderen ve commit()onları devam ettiren önceki cevaplar bana açık değildi. Tanımlar mantıklıdır, ancak sadece taahhüt etmek yerine neden bir floş kullanacağınız tanımlarından hemen net değildir.

Bir taahhüt her zaman temizlendiğinden ( https://docs.sqlalchemy.org/en/13/orm/session_basics.html#committing ) bu ses gerçekten benzer. Vurgulamak için büyük bir sorun, bir floş kalıcı değildir ve geri alınabilir, oysa bir taahhüt kalıcıdır, yani veritabanından son taahhüdü geri almasını isteyemezsiniz (sanırım)

@snapshoe, veritabanını sorgulamak ve yeni eklenen nesneler içeren sonuçlar almak istiyorsanız, önce temizlenmiş olmanız (veya sizin için kızarma işleminde bulunmanız gerekir) vurgulamaktadır. Belki de bazı insanlar için yararlıdır, ancak taahhüt etmek yerine neden yıkamak isteyeceğinizden emin değilim (geri alınabileceği önemsiz cevap dışında).

Başka bir örnekte, belgeleri yerel bir DB ile uzak sunucu arasında senkronize ediyordum ve kullanıcı iptal etmeye karar verdiyse, tüm ekleme / güncelleme / silme işlemleri geri alınmalıdır (yani kısmi senkronizasyon yok, sadece tam senkronizasyon yok). Tek bir belgeyi güncellerken eski satırı silmeye ve güncellenmiş sürümü uzak sunucudan eklemeye karar verdim. Sqlalchemy'nin yazılma şekli nedeniyle, taahhütte bulunurken işlem sırasının garanti edilmediği ortaya çıkıyor. Bu, yinelenen bir sürümün (eski sürümü silmeye çalışmadan önce) eklenmesiyle sonuçlandı, bu da DB'nin benzersiz bir kısıtlamanın başarısız olmasına neden oldu. Bu sorunu gidermek için flush(), sipariş korunur, ancak daha sonra senkronizasyon işlemi başarısız olursa geri alabilirdim.

Bu konudaki yazımı görün: sqlalchemy'de taahhütte bulunma yerine silme siparişi var mı

Benzer şekilde, birisi eklenti sırası işlemekle, ben eklemek eğer yani korunur olmadığını bilmek istiyordu object1sonra ekleyin object2, yok object1önce veritabanına eklenen olsun object2 sipariş tasarruf mu sqlalchemy oturumuna nesneleri eklerken?

Yine, burada muhtemelen bir flush () kullanımı istenen davranışı sağlayacaktır. Yani özet olarak, floş için bir kullanım, yine kendinize taahhüt sağlamayan bir "geri al" seçeneğine izin verirken, sipariş garantileri (sanırım) sağlamaktır.

Otomatik Yıkama ve Otomatik Kapatma

Otomatik yıkama, sorguların güncellenmeden önce sorguların güncellenmesini sağlamak için kullanılabilir, çünkü sqlalchemy sorguyu yürütmeden önce temizlenir. https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.params.autoflush

Otomatik tamamlama, tam olarak anlamadığım başka bir şey ama kullanımı önerilmez: https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.params. autocommit

Hafıza kullanımı

Şimdi asıl soru aslında bellekte yıkamanın yerine getirmenin etkilerini bilmek istiyordu. Kalıcı ya da değil yeteneği veritabanı sunduğu bir şey (sanırım), geri alma umurumda değil eğer sadece taahhüt veri tabanına boşaltmak için yeterli olmalıdır - taahhüt rağmen zarar (aslında muhtemelen yardımcı olur - aşağıya bakın) .

sqlalchemy temizlenmiş nesneler için zayıf referans kullanır: https://docs.sqlalchemy.org/en/13/orm/session_state_management.html#session-referencing-behavior

Bu, bir liste veya dikte gibi açıkça bir yerde tutulan bir nesneniz yoksa, sqlalchemy onu hafızada tutmayacaktır.

Ancak, o zaman endişelenecek şeylerin veritabanı tarafı var. Muhtemelen işlemeden yıkama, işlemi sürdürmek için bazı hafıza cezaları ile birlikte gelir. Yine, bu konuda yeniyim ama tam olarak bunu öneren bir bağlantı var: https://stackoverflow.com/a/15305650/764365

Başka bir deyişle, taahhütler bellek kullanımını azaltmalıdır, ancak muhtemelen burada bellek ve performans arasında bir denge vardır. Başka bir deyişle, muhtemelen her bir veritabanı değişikliğini tek tek yapmak istemezsiniz (performans nedenleriyle), ancak çok uzun süre beklemek bellek kullanımını artıracaktır.


1

Bu asıl soruya kesin olarak cevap vermiyor, ancak bazı insanlar session.autoflush = Truesizinle birlikte kullanmak zorunda olmadığından bahsetti session.flush()... Ve bu her zaman doğru değil.

Bir işlemin ortasında yeni oluşturulan bir nesnenin kimliğini kullanmak istiyorsanız, çağırmalısınız 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

Bunun nedeni autoflushyok DEĞİL (nesnenin bir sorgu bazen "Burada değil orada neden bu eserlerin?" Gibi karışıklığa neden olabilir, hangi Ama her ne kadar oto kimliği doldurmak snapshoe zaten bu kısmını örten).


Benim için oldukça önemli görünen ve gerçekten bahsedilmeyen ilgili bir yön:

Neden sürekli taahhüt etmiyorsun? - Cevap atomisitedir .

Bir fantezi kelimesi söylemek: için operasyonların bir topluluk var hepsi başarıyla yürütülebilir YA bunların hiçbiri etkili olur.

Örneğin, bir nesne (A) oluşturmak / güncellemek / silmek ve sonra başka bir (B) oluşturmak / güncellemek / silmek istiyorsanız, ancak (B) başarısız olursa (A) 'yı geri döndürmek istersiniz. Bu, bu 2 işlemin atomik olduğu anlamına gelir .

(B) (A) sonucunu ihtiyacı nedenle, aramak istediğiniz flush(A) sonra ve commit(B) sonra.

Ayrıca, session.autoflush is Trueyukarıda bahsettiğim durumlar veya Jimbo'nun cevabındaki diğerleri hariç , flushmanuel olarak aramanız gerekmeyecektir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.