it-swarm-eu.dev

Velmi pomalý DELETE v PostgreSQL, řešení?

Mám databázi PostgreSQL 9.2, která má hlavní schéma s přibližně 70 tabulkami a proměnným počtem identicky strukturovaných schémat pro každého klienta, každá s 30 tabulkami. Klientská schémata mají cizí klíče odkazující na hlavní schéma a nikoliv naopak.

Právě jsem začal naplňovat databázi některými reálnými daty převzatými z předchozí verze. DB dosáhla asi 1,5 GB (očekává se, že během několika týdnů vzroste na několik 10 s GB), když jsem musel provést hromadné smazání ve velmi centrální tabulce v hlavním schématu. Všechny příslušné cizí klíče jsou označeny NA VYMAZAT CASCADE.

Nebylo žádným překvapením, že by to trvalo dlouho, ale po 12 hodinách bylo jasné, že jsem měl lepší začít znovu, upustit DB a znovu zahájit migraci. Ale co když potřebuji opakovat tuto operaci později, když je databáze živá a mnohem větší? Existují alternativní a rychlejší metody?

Bylo by mnohem rychlejší, kdybych napsal skript, který bude procházet závislé tabulky, počínaje tabulkou nejvzdálenější od centrální tabulky a odstraňováním závislých řádků tabulku po tabulce?

Důležitým detailem je, že na některých tabulkách jsou spouštěče.

36
jd.

Měl jsem podobný problém. Jak se ukázalo, tyto spouštěče ON DELETE CASCADE Věci trochu zpomalily, protože tyto kaskádové delece byly hrozně pomalé.

Tento problém jsem vyřešil vytvořením indexů na polích cizího klíče v referenčních tabulkách a šel jsem z delta hodin na odstranění na několik sekund.

34
ailnlv

Máte několik možností. Nejlepší možností je spustit dávkové mazání tak, aby nedošlo k zasažení spouště. Před odstraněním deaktivujte spouštěče a znovu je povolte. To vám ušetří velmi velké množství času. Například:

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

Hlavním klíčem je, že chcete minimalizovat hloubku poddotazů. V takovém případě budete možná chtít nastavit dočasné tabulky pro ukládání relevantních informací, abyste se při smazání vyhnuli hlubokým podotázkám.

31
Chris Travers

Nejjednodušší metodou k vyřešení problému je dotaz na podrobné načasování z PostgreSQL: EXPLAIN . K tomu je třeba najít alespoň jeden dotaz, který se vyplní, ale trvá déle, než se očekávalo. Řekněme, že by tato linie vypadala

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

Místo skutečného spuštění tohoto příkazu můžete udělat

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

Vrácení na konci umožňuje spuštění bez skutečných úprav databáze, ale stále máte podrobné načasování toho, co trvalo. Po jeho spuštění můžete ve výstupu zjistit, že některá spoušť způsobuje obrovské zpoždění:

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

Hodnota time je v ms (milisekundách), takže kontrola tohoto kontrastu trvala asi 12,3 sekundy. Musíte přidat nový INDEX do požadovaných sloupců, aby bylo možné tento trigger efektivně vypočítat. U odkazů na cizí klíč musí být indexován odkaz na jinou tabulku (tj. Zdrojový sloupec, nikoli cílový sloupec). PostgreSQL tyto indexy automaticky nevytváří a DELETE je jediný běžný dotaz, kde tento index opravdu potřebujete. Výsledkem je, že jste mohli nahromadit roky dat, dokud nenarazíte na případ, kdy DELETE je příliš pomalý kvůli chybějícímu indexu.

Jakmile budete mít pevný výkon tohoto omezení (nebo nějaké jiné věci, která trvala příliš dlouho), opakujte příkaz v bloku begin/rollback, abyste mohli porovnat nový čas provádění s předchozím. Pokračujte, dokud nebudete spokojeni s dobou odezvy na odstranění jediného řádku (jeden dotaz mám přejít z 25,6 sekund na 15 ms jednoduše přidáním různých indexů). Poté můžete přistoupit k úplnému odstranění bez jakýchkoli hacků.

(Všimněte si, že EXPLAIN potřebuje dotaz, který lze úspěšně dokončit. Jednou jsem měl problém, kdy PostgreSQL trvalo příliš dlouho, než jsem zjistil, že jedno smazání bude porušovat omezení cizího klíče a v tom případě EXPLAIN nelze použít, protože nevydá načasování selhání dotazů. Nevím, jak snadno lze v takovém případě ladit problémy s výkonem.)

14

Vypnutí spouště může být hrozbou pro integritu DB a nelze jej doporučit; Pokud jste si však jisti, že vaše operace je odolná proti selhání, můžete deaktivovat spouštěče pomocí následujícího postupu:

SET session_replication_role = replica;

Spusťte DELETE zde.

Chcete-li obnovit triggery, spusťte:

SET session_replication_role = DEFAULT;

Zdroj zde.

9
Pinimo

Pokud máte aktivační události ON DELETE CASCADE, jsou tam pravděpodobně z nějakého důvodu, a proto by neměly být deaktivovány. Další trik (stále přidávejte své indexy), který pro mě funguje, je vytvoření funkce odstranění, která ručně odstraní data počínaje tabulkami na konci kaskády a pracuje směrem k hlavní tabulce. (Toto je to samé, co byste museli, kdybyste měli spouštěč ON DELETE RESTRICT)

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)
);

V tomto případě vymažte data v tabulce a poté tabulku a poté tabulku

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

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

$$ LANGUAGE SQL;
1
blindguy