Někdy potřebují databáze PostgreSQL importovat velké množství dat v jediném nebo minimálním počtu kroků. Toto je běžně známé jako hromadný import dat, kde zdrojem dat je obvykle jeden nebo více velkých souborů. Tento proces může být někdy nepřijatelně pomalý.
Existuje mnoho důvodů pro takový slabý výkon:indexy, spouštěče, cizí klíče, primární klíče GUID nebo dokonce protokol WAL (Write Ahead Log), to vše může způsobit zpoždění.
V tomto článku se budeme zabývat tipy na osvědčené postupy pro hromadný import dat do databází PostgreSQL. Mohou však nastat situace, kdy žádný z těchto tipů nebude účinným řešením. Čtenářům doporučujeme zvážit výhody a nevýhody jakékoli metody před jejím použitím.
Tip 1:Změňte cílovou tabulku na režim bez přihlášení
Pro PostgreSQL 9.5 a vyšší lze cílovou tabulku nejprve změnit na UNLOGGED a poté po načtení dat změnit zpět na LOGGED:
ALTER TABLE <target table> SET UNLOGGED
<bulk data insert operations…>
ALTER TABLE <target table> LOGGED
Režim UNLOGGED zajišťuje, že PostgreSQL neposílá operace zápisu tabulky do protokolu WAL (Write Ahead Log). To může výrazně urychlit proces načítání. Protože však operace nejsou protokolovány, nelze data obnovit, pokud během načítání dojde k havárii nebo nečistému vypnutí serveru. PostgreSQL po restartu automaticky zkrátí jakoukoli nepřihlášenou tabulku.
Také nepřihlášené tabulky nejsou replikovány na rezervní servery. V takových případech musí být existující replikace před načtením odstraněny a po načtení znovu vytvořeny. V závislosti na objemu dat v primárním uzlu a počtu pohotovostních režimů může být doba opětovného vytvoření replikace poměrně dlouhá a nepřijatelná z hlediska požadavků na vysokou dostupnost.
Pro hromadné vkládání dat do nezaprotokolovaných tabulek doporučujeme následující doporučené postupy:
- Provedení zálohy tabulky a dat před jejich změnou do režimu bez přihlášení
- Po dokončení načítání dat znovu vytvoříte replikaci na záložní servery
- Používání nepřihlášených hromadných vložek pro tabulky, které lze snadno znovu vyplnit (např. velké vyhledávací tabulky nebo tabulky dimenzí)
Tip 2:Odstraňte a znovu vytvořte indexy
Existující indexy mohou způsobit značné zpoždění při hromadném vkládání dat. Je to proto, že s každým přidáním řádku je třeba aktualizovat i odpovídající položku rejstříku.
Doporučujeme před spuštěním hromadného vložení vypustit indexy v cílové tabulce, kde je to možné, a po dokončení načítání indexy znovu vytvořit. Opět platí, že vytváření indexů ve velkých tabulkách může být časově náročné, ale bude obecně rychlejší než aktualizace indexů během načítání.
DROP INDEX <index_name1>, <index_name2> … <index_name_n>
<bulk data insert operations…>
CREATE INDEX <index_name> ON <target_table>(column1, …,column n)
Může být užitečné dočasně zvýšit maintenance_work_mem konfigurační parametr těsně před vytvořením indexů. Zvýšená pracovní paměť může pomoci rychleji vytvářet indexy.
Další možností, jak hrát na jistotu, je vytvořit kopii cílové tabulky ve stejné databázi s existujícími daty a indexy. Tuto nově zkopírovanou tabulku lze poté otestovat pomocí hromadného vkládání pro oba scénáře:indexy vypustit a znovu vytvořit nebo je dynamicky aktualizovat. Metodu, která poskytuje lepší výkon, lze poté použít pro aktivní tabulku.
Tip 3:Zahoďte a znovu vytvořte cizí klíče
Stejně jako indexy mohou omezení cizího klíče také ovlivnit výkon hromadného načítání. Je to proto, že každý cizí klíč v každém vloženém řádku musí být zkontrolován na existenci odpovídajícího primárního klíče. PostgreSQL v zákulisí používá k provedení kontroly spouštěč. Při načítání velkého počtu řádků musí být tento spouštěč spuštěn pro každý řádek, což zvyšuje režii.
Pokud to neomezují obchodní pravidla, doporučujeme vypustit všechny cizí klíče z cílové tabulky, načíst data v jediné transakci a poté cizí klíče po provedení transakce znovu vytvořit.
ALTER TABLE <target_table>
DROP CONSTRAINT <foreign_key_constraint>
BEGIN TRANSACTION
<bulk data insert operations…>
COMMIT
ALTER TABLE <target_table>
ADD CONSTRAINT <foreign key constraint>
FOREIGN KEY (<foreign_key_field>)
REFERENCES <parent_table>(<primary key field>)...
Ještě jednou, zvýšení maintenance_work_mem konfigurační parametr může zlepšit výkon opětovného vytváření omezení cizího klíče.
Tip 4:Deaktivujte spouštěče
Spouštěče INSERT nebo DELETE (pokud proces načítání zahrnuje také odstraňování záznamů z cílové tabulky) mohou způsobit zpoždění hromadného načítání dat. Je to proto, že každý spouštěč bude mít logiku, kterou je třeba zkontrolovat, a operace, které je třeba dokončit hned po VLOŽENÍ nebo VYMAZÁNÍ každého řádku.
Doporučujeme deaktivovat všechny spouštěče v cílové tabulce před hromadným načítáním dat a povolit je po dokončení načítání. Zakázání VŠECH spouštěčů zahrnuje také systémové spouštěče, které vynucují kontroly omezení cizího klíče.
ALTER TABLE <target table> DISABLE TRIGGER ALL
<bulk data insert operations…>
ALTER TABLE <target table> ENABLE TRIGGER ALL
Tip 5:Použijte příkaz COPY
Doporučujeme použít PostgreSQL COPY příkaz k načtení dat z jednoho nebo více souborů. COPY je optimalizována pro hromadné načítání dat. Je to efektivnější než spouštění velkého počtu příkazů INSERT nebo dokonce INSERT s více hodnotami.
COPY <target table> [( column1>, … , <column_n>)]
FROM '<file_name_and_path>'
WITH (<option1>, <option2>, … , <option_n>)
Mezi další výhody používání COPY patří:
- Podporuje import textových i binárních souborů
- Je to transakční povahy
- Umožňuje specifikovat strukturu vstupních souborů
- Může podmíněně načíst data pomocí klauzule WHERE
Tip 6:Použijte INSERT s více hodnotami
Spuštění několika tisíc nebo několika stovek tisíc příkazů INSERT může být špatnou volbou pro hromadné načítání dat. Je to proto, že každý jednotlivý příkaz INSERT musí být analyzován a připraven optimalizátorem dotazů, musí projít všemi kontrolami omezení, spustit jako samostatná transakce a přihlásit se do WAL. Použití jednoho příkazu INSERT s více hodnotami může tuto režii ušetřit.
INSERT INTO <target_table> (<column1>, <column2>, …, <column_n>)
VALUES
(<value a>, <value b>, …, <value x>),
(<value 1>, <value 2>, …, <value n>),
(<value A>, <value B>, …, <value Z>),
(<value i>, <value ii>, …, <value L>),
...
Výkon INSERT s více hodnotami je ovlivněn existujícími indexy. Před spuštěním příkazu doporučujeme zrušit indexy a poté je znovu vytvořit.
Další oblastí, kterou je třeba si uvědomit, je množství paměti dostupné pro PostgreSQL pro spouštění INSERTů s více hodnotami. Když je spuštěn INSERT s více hodnotami, musí se do paměti RAM vejít velké množství vstupních hodnot, a pokud není k dispozici dostatek paměti, proces může selhat.
Doporučujeme nastavit effective_cache_size parametr na 50 % a shared_buffer parametr na 25 % celkové paměti RAM stroje. Pro jistotu také spouští řadu vícehodnotových INSERTů, přičemž každý příkaz má hodnoty pro 1000 řádků.
Tip 7:Spusťte ANALYZE
Nesouvisí to se zlepšením výkonu hromadného importu dat, ale důrazně doporučujeme spustit ANALYZE příkaz na cílové tabulce ihned po hromadný dovoz. Velký počet nových řádků výrazně zkresluje rozložení dat ve sloupcích a způsobí, že všechny existující statistiky v tabulce budou zastaralé. Když optimalizátor dotazů používá zastaralé statistiky, může být výkon dotazu nepřijatelně nízký. Spuštěním příkazu ANALYZE zajistíte aktualizaci všech existujících statistik.
Poslední myšlenky
K hromadnému importu dat u databázové aplikace nemusí docházet každý den, ale při spuštění má dopad na výkon dotazů. Proto je nutné co nejlépe minimalizovat dobu načítání. Jedna věc, kterou mohou správci databází udělat, aby minimalizovali jakékoli překvapení, je testovat optimalizace zátěže ve vývojovém nebo pracovním prostředí s podobnými specifikacemi serveru a konfiguracemi PostgreSQL. Každý scénář načítání dat je jiný a nejlepší je vyzkoušet každou metodu a najít tu, která funguje.