PostgreSQL'de toplu güncelleme performansını optimize etme


37

Ubuntu 12.04'te PG 9.1 kullanılması.

Şu anda, formda olan bir veritabanında çok sayıda UPDATE ifadesi çalıştırmamız 24 saat sürüyor:

UPDATE table
SET field1 = constant1, field2 = constant2, ...
WHERE id = constid

(Sadece kimlik ile tanımlanan nesnelerin alanlarının üzerine yazıyoruz.) Değerler harici bir veri kaynağından geliyor (zaten bir tabloda DB'de değil).

Tablolarda, her birinin bir avuç endeksi vardır ve yabancı anahtar kısıtlamaları yoktur. Sonuna kadar hiçbir KOMİTE yapılmaz.

pg_dumpDB'nin tamamını içe aktarmak 2 saat sürer . Bu, makul şekilde hedef almamız gereken bir temel gibi görünüyor.

PostgreSQL'in yeniden içe aktarılması için bir veri kümesini bir şekilde yeniden yapılandıran özel bir program üretmemekle birlikte, toplu UPDATE performansını içe aktarma performansına yaklaştırmak için yapabileceğimiz bir şey var mı? (Bu, kütük yapılı birleştirme ağaçlarının iyi işlediğine inandığımız bir alandır, ancak PostgreSQL içinde yapabileceğimiz bir şey olup olmadığını merak ediyoruz.)

Bazı fikirler:

  • ID olmayan endekslerin düşmesi ve daha sonra yeniden inşası?
  • Artan checkpoint_segments, ancak bu aslında uzun vadeli iş hacminin sürdürülmesine yardımcı oluyor mu?
  • burada belirtilen teknikleri kullanarak ? (Yeni verileri tablo olarak yükle, ardından kimliğin yeni verilerde bulunmadığı eski verileri "birleştir"

Temel olarak denenecek bir sürü şey var ve en etkili olanın ne olduğundan ya da başka şeylere göz attığımızdan emin değiliz. Birkaç gün deneyerek geçireceğiz, ama burada da soracağımızı düşündük.

Masada eşzamanlı yüküm var ama salt okunur.


Sorunuzda çok önemli bilgiler eksik: Postgres sürümünüz? Değerler nereden geliyor? Veritabanının dışındaki bir dosya gibi görünüyor, ancak lütfen açıklığa kavuşturun. Hedef masaya eşzamanlı yükünüz var mı? Evet ise, tam olarak ne? Ya da bırakıp yeniden yaratmayı göze alabilir misiniz? Yabancı anahtar yok, tamam - görünümler gibi başka bağımlı nesneler var mı? Lütfen sorunuzu eksik bilgilerle düzenleyin. Yorumda sıkmayın.
Erwin Brandstetter

@ErwinBrandstetter Teşekkürler, sorumu güncelledi.
Yang

Sanırım explain analyzearama için bir dizin kullanıyor mu diye kontrol ettiniz ?
rogerdpack

Yanıtlar:


45

Varsayımlar

Q'da bilgiler eksik olduğundan, şunu varsayalım:

  • Verileriniz veritabanı sunucusundaki bir dosyadan gelir.
  • Veriler, COPYçıktı gibi , hedef tabloya uyacak şekilde satır başına benzersiz bir şekilde biçimlendirilir id.
    Değilse, önce doğru şekilde formatlayın veya COPYformatla başa çıkmak için seçenekleri kullanın.
  • Hedef tablodaki her bir satırı veya çoğunu güncelliyorsunuz.
  • Hedef masayı bırakıp yeniden oluşturabilirsiniz.
    Bu, eşzamanlı erişim yok demektir . Aksi takdirde, bu ilgili cevabı düşünün:
  • Endeksler dışında hiçbir bağımlı nesne yoktur.

Çözüm

Üçüncü merminizdeki bağlantıda belirtildiği gibi benzer bir yaklaşımla gitmenizi öneririm . Büyük optimizasyonlarla.

Geçici tabloyu oluşturmak için daha basit ve daha hızlı bir yol var:

CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;

Tek bir büyük UPDATEgeçici tablodan veritabanının birkaç büyüklük sırası daha hızlı veritabanı dışından bireysel güncellemeleri daha olacaktır.

In PostgreSQL'in MVCC modeli , bir UPDATEvasıta yeni bir satır sürümünü oluşturmak ve silindi olarak eskisini işaretlemek için. Bu bir INSERTve bir DELETEbirleşik kadar pahalı . Ayrıca, sizi birçok ölü çılgınlıkla bırakır. Zaten tüm tabloyu güncellediğiniz için, sadece yeni bir tablo oluşturmak ve eskisini bırakmak genel olarak daha hızlı olacaktır.

Eğer yeterince mevcut RAM, kümesine sahip temp_buffersRAM içinde geçici tablo tutmak için yeterince yüksek (sadece bu oturum için!) - Başka bir şey yapmadan önce.

Ne kadar RAM gerektiğine dair bir tahminde bulunmak için, küçük bir örnekle bir test yapın ve db nesne boyutu işlevlerini kullanın :

SELECT pg_size_pretty(pg_relation_size('tmp_tbl'));  -- complete size of table
SELECT pg_column_size(t) FROM tmp_tbl t LIMIT 10;  -- size of sample rows

Komut dosyasını tamamla

SET temp_buffers = '1GB';        -- example value

CREATE TEMP TABLE tmp_tbl AS SELECT * FROM tbl LIMIT 0;

COPY tmp_tbl FROM '/absolute/path/to/file';

CREATE TABLE tbl_new AS
SELECT t.col1, t.col2, u.field1, u.field2
FROM   tbl     t
JOIN   tmp_tbl u USING (id);

-- Create indexes like in original table
ALTER TABLE tbl_new ADD PRIMARY KEY ...;
CREATE INDEX ... ON tbl_new (...);
CREATE INDEX ... ON tbl_new (...);

-- exclusive lock on tbl for a very brief time window!
DROP TABLE tbl;
ALTER TABLE tbl_new RENAME TO tbl;

DROP TABLE tmp_tbl; -- will also be dropped at end of session automatically

Eşzamanlı yük

Masanın üzerindeki eşzamanlı işlemler (başlangıçtaki varsayımlara dahil etmediğim), masanın sonuna yakın kilitlendiği ve işlem yapıldığı anda başarısız olduğu için masadaki ismin derhal OID'sine çözüldüğü için bekleyecek, ancak Yeni tablonun farklı bir OID'si var. Tablo tutarlı kalır, ancak eşzamanlı işlemler bir istisna alabilir ve tekrarlanması gerekebilir. Bu cevaptaki detaylar:

GÜNCELLEME rotası

UPDATERotaya gitmek zorundaysanız , güncelleme sırasında gerekmeyen herhangi bir dizini bırakın ve daha sonra yeniden oluşturun. Tek bir parçada bir dizin oluşturmak, her bir satır için onu güncellemekten daha ucuzdur. Bu ayrıca HOT güncellemelerine de izin verebilir .

Ben kullanarak benzer bir prosedür özetlenen UPDATEiçinde SO bu yakından ilgili cevap .

 


1
Aslında sadece hedef tablodaki satırların% 20'sini güncelliyorum - hepsi değil, ama bir birleştirme muhtemelen rastgele güncelleme aramalarından daha iyi olacak kadar büyük bir bölüm.
Yang,

1
@AryehLeibTaurog: beri bu böyle olmamalıdır DROP TABLEbir alır Access Exclusive Lock. Her iki durumda da, cevabımın başındaki önkoşulları zaten sıraladım: You can afford to drop and recreate the target table.İşlemin başında masayı kilitlemenize yardımcı olabilir. Size durumunuzun tüm ayrıntılarını içeren yeni bir soru başlatmanızı öneriyorum .
Erwin Brandstetter

1
@ErwinBrandstetter İlginç. Sunucu sürümüne bağlı gibi görünüyor. Ben 8.4 ve 9.1 hatayı tekrar açığa çıkarmıştır psycopg2 adaptörü kullanarak ve psql'in istemcisi kullanarak . 9.3'te hata yok. İlk komut dosyasında yorumlarımı gör. Buraya gönderilecek bir soru olup olmadığından emin değilim, ancak postgresql listelerinden biriyle ilgili bazı bilgiler istemeye değer olabilir.
Aryeh Leib Taurog

1
İşlemi otomatikleştirmek için python'da basit bir yardımcı sınıf yazdım .
Aryeh Leib Taurog

3
Çok faydalı cevap. Biraz varyasyon olarak, bir orijinal tablodan haberdar olmak için sadece haberdar olmak için sütun ve referans sütunlar, silme sütunlu geçici tablo oluşturmak olabilir, o zaman tabloları kullanarak birleştirme CREATE TABLE tbl_new AS SELECT t.*, u.field1, u.field2 from tbl t NATURAL LEFT JOIN tmp_tbl u;, LEFT JOINhiçbir güncelleme var olduğu için satırları tutmak sağlar. Tabii ki NATURALherhangi bir geçerli USING()veya değiştirilebilir ON.
Skippy le Grand Gourou

2

Veriler yapılandırılmış bir dosyada hazırlanabilirse, yabancı bir veri paketleyicisiyle okuyabilir ve hedef tabloda bir birleştirme gerçekleştirebilirsiniz.


3
Özellikle "hedef tabloda birleştirme" ile ne kastediyorsunuz? Neden FDW'yi COPYing'den daha geçici bir tabloya sokmaktan daha iyi kullanıyorsunuz (orijinal sorudaki üçüncü mermide önerildiği gibi)?
Yang,

MERGE sql deyimindeki gibi "Birleştirme". FDW kullanmak, verileri geçici bir tabloya kopyalamak için ek bir adım olmadan bunu yapmanızı sağlar. Veri kümesinin tamamını değiştirmeyeceğinizi ve dosyada geçerli veri kümesinden bir değişikliği temsil etmeyecek belirli bir miktarda veri olacağına inanıyorum - önemli miktarda bir miktar değiştiyse tamamlandı. tablonun değiştirilmesi faydalı olabilir.
David Aldridge

1
@DavidAldridge: SQL: 2003 standardında tanımlanmış olmasına rağmen MERGE, PostgreSQL'de (henüz) uygulanmadı . Diğer RDBMS'deki uygulamalar oldukça değişkendir. İçin etiket bilgisi düşünün MERGEve UPSERT.
Erwin Brandstetter

@ErwinBrandstetter [glurk] Ah evet oldukça öyle. Pekala Merge, kekin üzerine krema koydum sanırım. Verileri geçici tabloya alma adımı olmadan erişmek, gerçekten FDW tekniğinin en önemli özelliğidir.
David Aldridge
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.