PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

7 Suggerimenti per le migliori pratiche per il caricamento in blocco di dati PostgreSQL

A volte, i database PostgreSQL devono importare grandi quantità di dati in un singolo o un numero minimo di passaggi. Questo è comunemente noto come importazione di dati in blocco in cui l'origine dati è in genere uno o più file di grandi dimensioni. Questo processo a volte può essere inaccettabilmente lento.

Ci sono molte ragioni per prestazioni così scarse:indici, trigger, chiavi esterne, chiavi primarie GUID o persino WAL (Write Ahead Log) possono causare ritardi.

In questo articolo, tratteremo alcuni suggerimenti di best practice per l'importazione in blocco di dati nei database PostgreSQL. Tuttavia, potrebbero esserci situazioni in cui nessuno di questi suggerimenti sarà una soluzione efficiente. Consigliamo ai lettori di considerare i pro ei contro di qualsiasi metodo prima di applicarlo.

Suggerimento 1:cambia la tabella di destinazione in modalità non registrata

Per PostgreSQL 9.5 e versioni successive, la tabella di destinazione può essere prima modificata in UNLOGGED, quindi ripristinata in LOGGED una volta caricati i dati:

ALTER TABLE <target table> SET UNLOGGED
<bulk data insert operations…>
ALTER TABLE <target table> LOGGED

La modalità UNLOGGED assicura che PostgreSQL non invii operazioni di scrittura di tabelle al WAL (Write Ahead Log). Ciò può rendere il processo di caricamento notevolmente veloce. Tuttavia, poiché le operazioni non vengono registrate, i dati non possono essere recuperati se si verifica un arresto anomalo o un arresto non corretto del server durante il caricamento. PostgreSQL troncherà automaticamente qualsiasi tabella non registrata una volta riavviato.

Inoltre, le tabelle non registrate non vengono replicate sui server di standby. In questi casi, le repliche esistenti devono essere rimosse prima del caricamento e ricreate dopo il caricamento. A seconda del volume di dati nel nodo principale e del numero di standby, il tempo per ricreare la replica potrebbe essere piuttosto lungo e non accettabile per i requisiti di alta disponibilità.

Consigliamo le seguenti best practice per l'inserimento collettivo di dati in tabelle non registrate:

  • Effettuare un backup della tabella e dei dati prima di modificarlo in modalità non registrata
  • Ricreare qualsiasi replica sui server di standby una volta completato il caricamento dei dati
  • Utilizzo di inserti in blocco non registrati per tabelle che possono essere facilmente ripopolate (ad es. tabelle di ricerca di grandi dimensioni o tabelle dimensionali)

Suggerimento 2:elimina e ricrea gli indici

Gli indici esistenti possono causare ritardi significativi durante gli inserimenti di dati in blocco. Questo perché quando ogni riga viene aggiunta, anche la voce di indice corrispondente deve essere aggiornata.

Si consiglia di eliminare gli indici nella tabella di destinazione, ove possibile, prima di avviare l'inserimento in blocco e di ricreare gli indici una volta completato il caricamento. Anche in questo caso, la creazione di indici su tabelle di grandi dimensioni può richiedere molto tempo, ma in genere sarà più veloce dell'aggiornamento degli indici durante il caricamento.

DROP INDEX <index_name1>, <index_name2> … <index_name_n>
<bulk data insert operations…>
CREATE INDEX <index_name> ON <target_table>(column1, …,column n)

Potrebbe essere utile aumentare temporaneamente il maintenance_work_mem parametro di configurazione appena prima di creare gli indici. La maggiore memoria di lavoro può aiutare a creare gli indici più velocemente.

Un'altra opzione per giocare sul sicuro è fare una copia della tabella di destinazione nello stesso database con dati e indici esistenti. Questa tabella appena copiata può essere quindi testata con l'inserimento in blocco per entrambi gli scenari:trascinare e ricreare gli indici o aggiornarli dinamicamente. Il metodo che produce prestazioni migliori può essere quindi seguito per la tabella live.

Suggerimento 3:rilascia e ricrea le chiavi esterne

Come gli indici, anche i vincoli di chiave esterna possono influire sulle prestazioni del caricamento di massa. Questo perché ogni chiave esterna in ogni riga inserita deve essere verificata per l'esistenza di una chiave primaria corrispondente. Dietro le quinte, PostgreSQL utilizza un trigger per eseguire il controllo. Quando si carica un numero elevato di righe, questo trigger deve essere attivato per ogni riga, aumentando l'overhead.

A meno che non sia limitato dalle regole aziendali, consigliamo di eliminare tutte le chiavi esterne dalla tabella di destinazione, caricare i dati in un'unica transazione, quindi ricreare le chiavi esterne dopo aver eseguito il commit della transazione.

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

Ancora una volta, aumentando il maintenance_work_mem parametro di configurazione può migliorare le prestazioni di ricreazione dei vincoli di chiave esterna.

Suggerimento 4:disabilita i trigger

I trigger INSERT o DELETE (se il processo di caricamento prevede anche l'eliminazione di record dalla tabella di destinazione) possono causare ritardi nel caricamento in blocco dei dati. Questo perché ogni trigger avrà la logica che deve essere controllata e le operazioni che devono essere completate subito dopo che ogni riga è stata INSERT o DELETEd.

Si consiglia di disabilitare tutti i trigger nella tabella di destinazione prima del caricamento in blocco dei dati e di abilitarli al termine del caricamento. La disabilitazione di TUTTI i trigger include anche i trigger di sistema che impongono i controlli dei vincoli di chiave esterna.

ALTER TABLE <target table> DISABLE TRIGGER ALL
<bulk data insert operations…>
ALTER TABLE <target table> ENABLE TRIGGER ALL

Suggerimento 5:usa il comando COPIA

Ti consigliamo di utilizzare la COPIA di PostgreSQL comando per caricare i dati da uno o più file. COPY è ottimizzato per i carichi di dati in blocco. È più efficiente dell'esecuzione di un gran numero di istruzioni INSERT o anche di INSERT multivalore.

COPY <target table> [( column1>, … , <column_n>)]
FROM  '<file_name_and_path>' 
WITH  (<option1>, <option2>, … , <option_n>)

Altri vantaggi dell'utilizzo di COPY includono:

  • Supporta sia l'importazione di file di testo che binari
  • È di natura transazionale
  • Permette di specificare la struttura dei file di input
  • Può caricare dati condizionalmente utilizzando una clausola WHERE

Suggerimento 6:usa INSERT multivalore

L'esecuzione di diverse migliaia o diverse centinaia di migliaia di istruzioni INSERT può essere una scelta sbagliata per il caricamento di massa di dati. Questo perché ogni singolo comando INSERT deve essere analizzato e preparato da Query Optimizer, passare attraverso tutto il controllo dei vincoli, essere eseguito come transazione separata e registrato nel WAL. L'utilizzo di una singola istruzione INSERT multivalore può risparmiare questo sovraccarico.

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>),
...

Le prestazioni INSERT multivalore sono influenzate dagli indici esistenti. Si consiglia di eliminare gli indici prima di eseguire il comando e di ricreare gli indici in seguito.

Un'altra area da tenere presente è la quantità di memoria disponibile in PostgreSQL per l'esecuzione di INSERT multivalore. Quando viene eseguito un INSERT multivalore, un gran numero di valori di input deve rientrare nella RAM e, a meno che non sia disponibile memoria sufficiente, il processo potrebbe non riuscire.

Ti consigliamo di impostare la dimensione_cache_effettiva parametro al 50% e buffer_condiviso parametro al 25% della RAM totale della macchina. Inoltre, per sicurezza, esegue una serie di INSERT multivalore con ogni istruzione con valori per 1000 righe.

Suggerimento 7:esegui ANALIZZA

Ciò non è correlato al miglioramento delle prestazioni dell'importazione di dati in blocco, ma consigliamo vivamente di eseguire ANALYZE comando sulla tabella di destinazione subito dopo l'importazione in blocco. Un numero elevato di nuove righe distorcerà notevolmente la distribuzione dei dati nelle colonne e renderà obsolete tutte le statistiche esistenti sulla tabella. Quando Query Optimizer utilizza statistiche obsolete, le prestazioni delle query possono essere inaccettabilmente scarse. L'esecuzione del comando ANALYZE garantirà l'aggiornamento di tutte le statistiche esistenti.

Pensieri finali

L'importazione di dati in blocco potrebbe non avvenire tutti i giorni per un'applicazione di database, ma c'è un impatto sulle prestazioni delle query durante l'esecuzione. Ecco perché è necessario ridurre al minimo i tempi di caricamento nel miglior modo possibile. Una cosa che i DBA possono fare per ridurre al minimo qualsiasi sorpresa è testare le ottimizzazioni del carico in un ambiente di sviluppo o staging con specifiche server simili e configurazioni PostgreSQL. Ogni scenario di caricamento dei dati è diverso ed è meglio provare ogni metodo e trovare quello che funziona.