PostgreSQL'de çok yavaş bir DELETE, geçici çözüm?


30

PostgreSQL 9.2'de, yaklaşık 70 tablodan oluşan bir ana şema ve her biri 30 tablodan oluşan değişken yapılandırılmış müşteri başına şema sayısı içeren bir veri tabanına sahibim. İstemci şemalarında ana şemaya atıf yapan yabancı anahtarlar var, bunun tersi olmaz.

Veritabanını önceki sürümden alınan bazı gerçek verilerle doldurmaya başladım. Ana şemada çok merkezi bir tabloda toplu bir silme işlemi yapmak zorunda kaldığımda DB yaklaşık 1,5 GB'a ulaşmıştı (haftalar içinde birkaç 10 GB'a çıkması bekleniyor). İlgili tüm yabancı anahtarlar DELETE CASCADE ON olarak işaretlenmiştir.

Bunun uzun sürmesi şaşırtıcı değildi, ancak 12 saat sonra baştan başladığımda, DB'yi düşürdüğümden ve göçü tekrar başlattığım daha açıktı. Ancak, DB canlı ve daha büyük olduğunda bu işlemi daha sonra tekrarlamak gerekirse ne olur? Alternatif, daha hızlı yöntemler var mı?

Ortadaki tablodan en uzaktaki tablodan başlayarak, bağımlı satırları tabloyu silerek bağımlı tablolara göz atacak bir senaryo yazsam daha hızlı olur mu?

Önemli bir ayrıntı, bazı tablolarda tetikleyiciler olduğudur.


4
5 yıl sonra kabul edilen cevabı değiştiriyorum. Yavaş SİLME neredeyse her zaman yabancı anahtarlardaki eksik dizinlerden, silinen tabloya doğrudan veya dolaylı olarak atıfta bulunan kaynaklanır. Çözüm, neredeyse her zaman daha hızlı çalışmasını sağlamak için (örneğin, eksik dizinler ekleyerek) ve neredeyse hiçbir zaman tüm tetikleyicileri devre dışı bırakmamakla birlikte, DELETE ifadelerine ateş açan tetikleyiciler işleri de yavaşlatabilir.
jd.

Yanıtlar:


30

Ben de benzer bir problem yaşadım. Görünüşe göre, bu ON DELETE CASCADEtetikleyiciler işleri biraz yavaşlatıyordu, çünkü kademeli silmeleri oldukça yavaştı.

Referans tablolarındaki yabancı anahtar alanlarda indeksler oluşturarak bu sorunu çözdüm ve birkaç saniye silmek için birkaç saat ayırdım.


Vay, bu bana 8M kayıtlarını birkaç dakikada silmeme yardımcı oldu. Ama anlamadığım şey, masamın sadece diğer masalara referanslar verdiği, başka masaların da masaya referansları olmadığıdır. Peki buradaki etki tam olarak nedir? ( ON DELETE CASCADE
Kullanmıyorum

2
Bu benim için de çözdü. Bunu deneyen herkes EXPLAIN (ANALYZE, BUFFERS)için, tek bir satır silme işlemi ile ilgili bir sorgu yapabilirsiniz ve hangi yabancı anahtar kısıtlamalarının en uzun sürdüğünü göstermelidir (en azından benim için yaptı).
Justin Workman,

Aynı, kaskad 600k satırlarında silmek zorunda kaldı ve başlangıçta% 100 CPU kullanımı ile işlem başına 2-10 arasında alıyordu. Şimdi% 80 CPU kullanımıyla hepsini silmek sadece birkaç dakika sürdü.
fillobotto

Herhangi bir yere yabancı bir referansınız varsa, kaynak sütununun gerçek endekse sahip olması gerektiğini unutmayın, aksi halde performans zarar görür. PRIMARYEndeks yeterli olup olmadığından emin değilim , ancak UNIQUEendeks kesinlikle bu amaç için yeterince iyi değil.
Mikko Rantalainen

26

Birkaç seçeneğin var. En iyi seçenek, toplu iş silme işlemini başlatmaktır; böylece tetikleyiciler etkilenmez. Silmeden önce tetikleyicileri devre dışı bırakın, ardından yeniden etkinleştirin. Bu size çok fazla zaman kazandırır. Örneğin:

ALTER TABLE tablename DISABLE TRIGGER ALL; 
DELETE ...; 
ALTER TABLE tablename ENABLE TRIGGER ALL;

Buradaki ana anahtar, alt sorguların derinliğini en aza indirmek istediğinizdir. Bu durumda, ilgili bilgileri saklamak için geçici tablolar oluşturmak isteyebilirsiniz, böylece silme işleminizde derin alt sorguları önleyebilirsiniz.


Benim durumumda, yatmadan önce DELOM FROM komutunu başlattım ve ertesi gün bilgisayarıma döndüğümde hala yapılmadı. Her zaman bir çekirdekte% 100 CPU kullanımı. Tetikleyicileri devre dışı bıraktıktan ve yeniden denedikten sonra 200k kayıtları silmek 3 saniye sürdü. Teşekkür ederim!
Nick Woodhams

13

Sorunu çözmek için en kolay yöntem PostgreSQL'den ayrıntılı zamanlama sorgulamak için: EXPLAIN. Bunun için en azından tamamlanan ancak beklenenden daha uzun süren bir sorgu bulmanız gerekir. Diyelim ki bu çizgi benziyor

delete from mydata where id='897b4dde-6a0d-4159-91e6-88e84519e6b6';

Gerçekten bu komutu çalıştırmak yerine yapabilecekleriniz

begin;
explain (analyze,buffers,timing) delete from mydata where id='897b4dde-6a0d-4159-91e6-88e84519e6b6';
rollback;

Sonunda geri alma, bunu veritabanını gerçekten değiştirmeden çalıştırmaya izin verir, ancak ne kadar sürdüğünün ayrıntılı zamanlamasını hala elde edersiniz. Bunu çalıştırdıktan sonra, çıktıda bazı tetikleyicilerin büyük gecikmelere neden olduğunu görebilirsiniz:

...
Trigger for constraint XYZ123: time=12311.292 calls=1
...

timeBu contraint kontrol 12.3 saniyede aldı ms (milisaniye) bulunmaktadır. INDEXBu tetikleyicinin etkili bir şekilde hesaplanması için gerekli sütunların üzerine yeni bir ekleme yapmanız gerekir . Yabancı anahtar referansları için başka bir tabloya referans veren sütunun indekslenmesi gerekir (yani, hedef sütunu değil, kaynak sütunu). PostgreSQL sizin için otomatik olarak bu tür dizinler oluşturmaz ve DELETEgerçekten bu dizine gerçekten ihtiyacınız olan tek sorgudur. Sonuç olarak, DELETEbir endeks eksik olduğundan dolayı çok yavaş olan davaya ulaşana kadar yıllarca veri birikmiş olabilirsiniz .

Bu kısıtlamanın (veya çok uzun süren başka bir şeyin) performansını düzelttikten sonra, begin/ rollbackblok komutunu tekrarlayın, böylece yeni yürütme zamanını önceki ile karşılaştırabilirsiniz. Tek satır silme yanıt süresinden memnun kalana kadar devam edin (yalnızca farklı dizinler ekleyerek 25.6 saniyeden 15 ms'ye kadar bir sorgun var). Sonra silme işleminizi herhangi bir hack olmadan tamamlayabilirsiniz.

( EXPLAINBaşarılı bir şekilde tamamlayabilen bir sorguya ihtiyaç duyulduğunu unutmayın . Bir keresinde PostgreSQL'in bir silme işleminin bir yabancı anahtar kısıtlamasını ihlal edeceğini anlamak için çok uzun sürdüğü bir sorun vardı ve bu durumda EXPLAINbaşarısızlık için zamanlama vermeyeceği için kullanılamıyordu. sorgular. Böyle bir durumda performans sorunlarını ayıklamak için kolay bir yol bilmiyorum.)


8

Tetikleyicileri devre dışı bırakmak, DB bütünlüğüne yönelik bir tehdit olabilir ve önerilemez; ancak, işleminizin kısıtlamaya-dayanıklı olmadığından eminseniz, tetikleyicileri aşağıdakilerle devre dışı bırakabilirsiniz:SET session_replication_role = replica;

DELETEBurayı çalıştır .

Tetikleyicileri geri yüklemek için aşağıdakileri çalıştırın: SET session_replication_role = DEFAULT;

Kaynak buraya.


0

ON DELETE CASCADE tetikleyicileri varsa, umarım bir sebepten dolayı oradadır ve bu nedenle devre dışı bırakılmamalıdır. Benim için çalışan başka bir püf noktası (yine de endekslerinizi ekleyin), kademenin sonundaki tablolarla başlayan verileri manuel olarak silen ve ana tabloya doğru çalışan bir silme işlevi oluşturmaktır. (Bu bir ON DELET RESTRICT tetikleyicisine sahip olmanız durumunda yapmanız gerekenlerle aynıdır)

CREATE TABLE tablea (
    tablea_uid integer
);

CREATE TABLE tableb (
    tableb_uid integer,
    tablea_rid integer REFERENCES tablea(tablea_uid)
);

CREATE TABLE tablec (
    tablec_uid integer,
    tableb_rid integer REFERENCES tableb(tableb_uid)
);

Bu durumda, tablodaki verileri sonra tabloyu ve ardından tabloyu silin.

CREATE OR REPLACE FUNCTION delete_in_order()
 RETURNS void AS $$

    DELETE FROM tablec;
    DELETE FROM tableb;
    DELETE FROM tablea;

$$ LANGUAGE SQL;
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.