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é.
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.
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.
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.