it-swarm-eu.dev

Proč Postgres UPDATE trvalo 39 hodin?

Mám Postgresovou tabulku s ~ 2,1 miliony řádků. Spustil jsem níže uvedenou aktualizaci:

WITH stops AS (
    SELECT id,
           rank() OVER (ORDER BY offense_timestamp,
                     defendant_dl,
                     offense_street_number,
                     offense_street_name) AS stop
    FROM   consistent.master
    WHERE  citing_jurisdiction=1
)

UPDATE consistent.master
SET arrest_id=stops.stop
FROM stops
WHERE master.id = stops.id;

Spuštění tohoto dotazu trvalo 39 hodin. Používám to na 4 (fyzickém) jádrovém přenosném procesoru i7 Q720, spoustě paměti RAM, nic jiného běžícím po většinu času. Žádné omezení prostoru na pevném disku. Tabulka byla nedávno vysátá, analyzována a znovuindexována.

Po celou dobu, kdy byl dotaz spuštěn, alespoň po dokončení počátečního WITH, bylo využití CPU obvykle nízké a HDD byl používán 100%. HDD byl používán tak tvrdě, že všechny ostatní aplikace běžely podstatně pomaleji než obvykle.

Nastavení napájení notebooku bylo zapnuto vysoký výkon (Windows 7 x64).

Zde je EXPLAIN:

Update on master  (cost=822243.22..1021456.89 rows=2060910 width=312)
  CTE stops
    ->  WindowAgg  (cost=529826.95..581349.70 rows=2060910 width=33)
          ->  Sort  (cost=529826.95..534979.23 rows=2060910 width=33)
                Sort Key: consistent.master.offense_timestamp, consistent.master.defendant_dl, consistent.master.offense_street_number, consistent.master.offense_street_name
                ->  Seq Scan on master  (cost=0.00..144630.06 rows=2060910 width=33)
                      Filter: (citing_jurisdiction = 1)
  ->  Hash Join  (cost=240893.51..440107.19 rows=2060910 width=312)
        Hash Cond: (stops.id = consistent.master.id)
        ->  CTE Scan on stops  (cost=0.00..41218.20 rows=2060910 width=48)
        ->  Hash  (cost=139413.45..139413.45 rows=2086645 width=268)
              ->  Seq Scan on master  (cost=0.00..139413.45 rows=2086645 width=268)

citing_jurisdiction=1 Vylučuje pouze několik desítek tisíc řádků. I s touto klauzulí WHERE stále pracuji na více než 2 milionech řádků.

Pevný disk je celý šifrován pomocí TrueCrypt 7.1a. To trochu zpomalí, ale ne dost, aby způsobil, že dotaz zabere , který mnoho hodin.

Spuštění části WITH trvá pouze asi 3 minuty.

Pole arrest_id Nemělo žádný index pro cizí klíč. V této tabulce je 8 indexů a 2 cizí klíče. Všechna ostatní pole v dotazu jsou indexována.

Pole arrest_id Nemělo žádná omezení kromě NOT NULL.

Tabulka obsahuje celkem 32 sloupců.

arrest_id Je typu měnící se charakter (20). Uvědomuji si, že rank() produkuje číselnou hodnotu, ale musím použít měnící se znak (20), protože mám další řádky, kde citing_jurisdiction<>1, Které používají nečíselná data pro toto pole.

Pole arrest_id Bylo prázdné pro všechny řádky s citing_jurisdiction=1.

Jedná se o osobní, high-end (před 1 rokem) notebook. Jsem jediný uživatel. Nebyly spuštěny žádné další dotazy ani operace. Blokování se zdá nepravděpodobné.

V této tabulce nebo kdekoli jinde v databázi nejsou žádné spouštěče.

Jiné operace v této databázi nikdy nezabírají čas. Při správném indexování jsou dotazy SELECT obvykle velmi rychlé.

17
Aren Cambre

Nedávno jsem měl něco podobného s tabulkou 3,5 milionu řádků. Moje aktualizace by nikdy nedokončila. Po mnoha experimentech a frustrace jsem konečně našel viníka. Ukázalo se, že se jedná o indexy v aktualizované tabulce.

Řešením bylo zrušit všechny indexy v aktualizované tabulce před spuštěním příkazu aktualizace. Jakmile jsem to udělal, aktualizace skončila za pár minut. Po dokončení aktualizace jsem znovu vytvořil indexy a vrátil se zpět do práce. Pravděpodobně vám to v tuto chvíli nepomůže, ale možná někdo, kdo hledá odpovědi.

Nechal bych indexy na stole, ze kterého stahujete data. Ten nebude muset nadále aktualizovat žádné indexy a měl by pomoci s hledáním dat, která chcete aktualizovat. Na pomalém notebooku to fungovalo dobře.

19
JC Avena

Váš největší problém spočívá v tom, že na pevném disku přenosného počítače vykonáte obrovské množství náročné práce na hledání. To nikdy nebude rychlé bez ohledu na to, co děláte, zejména pokud je to druh pomalejší jednotky 5400RPM dodávané v mnoha laptopech.

TrueCrypt zpomaluje věci více než „trochu“ pro zápisy. Čtení bude poměrně rychlé, ale zápisy způsobí, že RAID 5 bude vypadat rychle. Spuštění DB na svazku TrueCrypt bude mučením pro zápisy, zejména náhodné zápisy.

V tomto případě si myslím, že byste ztratili čas pokusem o optimalizaci dotazu. Stejně tak přepisujete většinu řádků a bude to pomalé s vaší děsivou situací zápisu. Co bych doporučil, je:

BEGIN;
SELECT ... INTO TEMPORARY TABLE master_tmp ;
TRUNCATE TABLE consistent.master;
-- Now DROP all constraints on consistent.master, then:
INSERT INTO consistent.master SELECT * FROM master_tmp;
-- ... and re-create any constraints.

Mám podezření, že to bude rychlejší než pouhé zrušení a opětovné vytvoření omezení, protože UPDATE bude mít poměrně náhodné vzory zápisu, které zabijí vaše úložný prostor. Dva hromadné vložky, jeden do nelogované tabulky a jeden do tabulky s protokoly WAL bez omezení, budou pravděpodobně rychlejší.

Pokud máte naprosto aktuální zálohy a nevadí vám obnovit databázi ze záloh , můžete také spustit PostgreSQL pomocí fsync=off parametr a full_page_writes=off dočasně pro tuto hromadnou operaci. Jakýkoli neočekávaný problém, jako je ztráta energie nebo selhání operačního systému, ponechá vaši databázi neopravitelnou, zatímco fsync=off.

Ekvivalent POSTGreSQL jako „no logging“ znamená použít nepřihlášené tabulky. Tyto nepřihlášené tabulky se zkrátí, pokud se DB nečistě vypne, zatímco jsou špinavé. Použití nepřihlášených tabulek sníží alespoň polovinu zátěže zápisu a sníží počet vyhledávání, takže mohou být o HODNĚ rychlejší.

Stejně jako v Oracle může být dobrý nápad zrušit index a po velké dávkové aktualizaci jej znovu vytvořit. Plánovač PostgreSQL nedokáže zjistit, že probíhá velká aktualizace, pozastavit aktualizace indexu a na konci znovu vytvořit index; i kdyby to bylo možné, bylo by pro něj velmi těžké zjistit, v jakém okamžiku se to vyplatilo, zejména předem.

15
Craig Ringer

Někdo dá lepší odpověď na Postgres, ale zde je několik pozorování z pohledu Oracle, které se mohou vztahovat (a komentáře jsou příliš dlouhé pro pole komentářů).

Moje první starost by byla snaha aktualizovat 2 miliony řádků v jedné transakci. V systému Oracle byste psali obraz před každým aktualizovaným blokem, takže další relace bude mít stále konzistentní čtení bez přečtení vašich upravených bloků a budete mít možnost vrátit se zpět. To je dlouhý návrat zpět. Obvykle je lepší provádět transakce v malých kouscích. Řekněte najednou 1 000 záznamů.

Pokud máte na stole indexy a tabulka bude během údržby považována za nefunkční, je často lepší odstranit indexy před velkou operací a poté ji znovu vytvořit. Levnější pak se neustále snaží udržovat indexy s každým aktualizovaným záznamem.

Oracle umožňuje "no logging" rady k výpisům zastavit deníku. Zrychluje příkazy hodně, ale ponechá váš db v „neopravitelné“ situaci. Chtěli byste tedy dříve zálohovat a okamžitě poté zálohovat. Nevím, jestli má Postgres podobné možnosti.

2
Glenn