Nogle gange skal PostgreSQL-databaser importere store mængder data i et enkelt eller et minimalt antal trin. Dette er almindeligvis kendt som massedataimport, hvor datakilden typisk er en eller flere store filer. Denne proces kan nogle gange være uacceptabel langsom.
Der er mange årsager til så dårlig ydeevne:indekser, triggere, fremmednøgler, GUID primære nøgler eller endda Write Ahead Log (WAL) kan alle forårsage forsinkelser.
I denne artikel vil vi dække nogle tips til bedste praksis til masseimport af data til PostgreSQL-databaser. Der kan dog være situationer, hvor ingen af disse råd vil være en effektiv løsning. Vi anbefaler, at læsere overvejer fordele og ulemper ved enhver metode, før de anvender den.
Tip 1:Skift måltabel til ikke-logget tilstand
For PostgreSQL 9.5 og nyere kan måltabellen først ændres til UNLOGGED og derefter ændres tilbage til LOGGED, når dataene er indlæst:
ALTER TABLE <target table> SET UNLOGGED
<bulk data insert operations…>
ALTER TABLE <target table> LOGGED
UNLOGGED-tilstanden sikrer, at PostgreSQL ikke sender tabelskriveoperationer til Write Ahead Log (WAL). Dette kan gøre indlæsningsprocessen betydeligt hurtig. Men da operationerne ikke logges, kan data ikke gendannes, hvis der er et nedbrud eller uren servernedlukning under indlæsningen. PostgreSQL vil automatisk afkorte enhver ulogget tabel, når den genstarter.
Også uloggede tabeller replikeres ikke til standby-servere. I sådanne tilfælde skal eksisterende replikationer fjernes før belastningen og genskabes efter belastningen. Afhængigt af mængden af data i den primære node og antallet af standbyer, kan tiden til genskabelse af replikering være ret lang og ikke acceptabel af høje tilgængelighedskrav.
Vi anbefaler følgende bedste praksis for masseindsættelse af data i ikke-loggede tabeller:
- Tag en sikkerhedskopi af tabellen og data, før du ændrer den til en ikke-logget tilstand
- Genskabelse af enhver replikering til standby-servere, når dataindlæsningen er fuldført
- Brug af ikke-loggede masseindlæg til tabeller, som nemt kan genudfyldes (f.eks. store opslagstabeller eller dimensionstabeller)
Tip 2:Slip og genskab indekser
Eksisterende indekser kan forårsage betydelige forsinkelser under massedataindsættelser. Dette skyldes, at efterhånden som hver række tilføjes, skal den tilsvarende indeksindgang også opdateres.
Vi anbefaler at droppe indekser i måltabellen, hvor det er muligt, før du starter masseindsættelsen, og genskabe indekserne, når indlæsningen er fuldført. Igen kan det være tidskrævende at oprette indekser på store tabeller, men det vil generelt være hurtigere end at opdatere indekserne under indlæsning.
DROP INDEX <index_name1>, <index_name2> … <index_name_n>
<bulk data insert operations…>
CREATE INDEX <index_name> ON <target_table>(column1, …,column n)
Det kan være umagen værd midlertidigt at øge maintenance_work_mem konfigurationsparameter lige før oprettelse af indekserne. Den øgede arbejdshukommelse kan hjælpe med at skabe indekserne hurtigere.
En anden mulighed for at spille sikkert er at lave en kopi af måltabellen i den samme database med eksisterende data og indekser. Denne nyligt kopierede tabel kan derefter testes med bulkinsert for begge scenarier:drop-og-genopret indekser eller dynamisk opdatering af dem. Metoden, der giver bedre ydeevne, kan derefter følges til live-tabellen.
Tip 3:Slip og genskab fremmednøgler
Ligesom indekser kan begrænsninger af fremmednøgle også påvirke bulk load ydeevne. Dette skyldes, at hver fremmednøgle i hver indsat række skal kontrolleres for eksistensen af en tilsvarende primærnøgle. Bag scenen bruger PostgreSQL en trigger til at udføre kontrollen. Når du indlæser et stort antal rækker, skal denne trigger udløses for hver række, hvilket øger overheaden.
Medmindre det er begrænset af forretningsregler, anbefaler vi at slette alle fremmednøgler fra måltabellen, indlæse dataene i en enkelt transaktion og derefter genskabe fremmednøglerne efter at have foretaget transaktionen.
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>)...
Endnu en gang øger du maintenance_work_mem konfigurationsparameter kan forbedre ydeevnen ved at genskabe fremmednøglebegrænsninger.
Tip 4:Deaktiver triggere
INSERT- eller DELETE-triggere (hvis indlæsningsprocessen også involverer sletning af poster fra måltabellen) kan forårsage forsinkelser i indlæsning af massedata. Dette skyldes, at hver trigger vil have logik, der skal kontrolleres, og operationer, der skal fuldføres lige efter, at hver række er INSERT eller DELETED.
Vi anbefaler, at du deaktiverer alle triggere i måltabellen før masseindlæsning af data og aktiverer dem, efter at indlæsningen er afsluttet. Deaktivering af ALLE udløsere omfatter også systemudløsere, der gennemtvinger kontrol af fremmednøglebegrænsninger.
ALTER TABLE <target table> DISABLE TRIGGER ALL
<bulk data insert operations…>
ALTER TABLE <target table> ENABLE TRIGGER ALL
Tip 5:Brug COPY-kommando
Vi anbefaler at bruge PostgreSQL COPY kommando til at indlæse data fra en eller flere filer. COPY er optimeret til massedataindlæsninger. Det er mere effektivt end at køre et stort antal INSERT-sætninger eller endda INSERTS med flere værdier.
COPY <target table> [( column1>, … , <column_n>)]
FROM '<file_name_and_path>'
WITH (<option1>, <option2>, … , <option_n>)
Andre fordele ved at bruge COPY inkluderer:
- Den understøtter import af både tekst og binær fil
- Det er af transaktionsmæssig karakter
- Det gør det muligt at specificere strukturen af inputfilerne
- Den kan betinget indlæse data ved hjælp af en WHERE-sætning
Tip 6:Brug INSERT med flere værdier
At køre flere tusinde eller flere hundrede tusinder af INSERT-sætninger kan være et dårligt valg til bulk dataindlæsning. Det skyldes, at hver enkelt INSERT-kommando skal analyseres og forberedes af forespørgselsoptimeringsværktøjet, gennemgå alle begrænsningskontrollen, køres som en separat transaktion og logges på WAL. Brug af en enkelt INSERT-sætning med flere værdier kan gemme denne overhead.
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>),
...
Multi-valued INSERT ydeevne påvirkes af eksisterende indekser. Vi anbefaler at droppe indekserne, før du kører kommandoen, og genskabe indekserne bagefter.
Et andet område, du skal være opmærksom på, er mængden af hukommelse, der er tilgængelig for PostgreSQL til at køre INSERT'er med flere værdier. Når en INSERT med flere værdier køres, skal et stort antal inputværdier passe i RAM'en, og medmindre der er tilstrækkelig hukommelse tilgængelig, kan processen mislykkes.
Vi anbefaler at indstille effective_cache_size parameter til 50 % og delt_buffer parameter til 25 % af maskinens samlede RAM. For en sikkerheds skyld kører den også en række INSERT'er med flere værdier, hvor hver sætning har værdier for 1000 rækker.
Tip 7:Kør ANALYSE
Dette er ikke relateret til at forbedre ydeevnen for import af massedata, men vi anbefaler kraftigt at køre ANALYSE kommando på måltabellen umiddelbart efter masseimporten. Et stort antal nye rækker vil skævvride datafordelingen i kolonner betydeligt og vil medføre, at eksisterende statistik på tabellen er forældet. Når forespørgselsoptimeringsværktøjet bruger forældede statistikker, kan forespørgselsydeevne være uacceptabelt dårlig. Kørsel af kommandoen ANALYSE vil sikre, at alle eksisterende statistikker opdateres.
Sidste tanker
Massedataimport finder muligvis ikke sted hver dag for en databaseapplikation, men der er en ydeevnepåvirkning på forespørgsler, når den kører. Derfor er det nødvendigt at minimere indlæsningstiden bedst muligt. En ting DBA'er kan gøre for at minimere enhver overraskelse, er at teste belastningsoptimeringerne i et udviklings- eller iscenesættelsesmiljø med lignende serverspecifikationer og PostgreSQL-konfigurationer. Alle dataindlæsningsscenarier er forskellige, og det er bedst at prøve hver metode og finde den, der virker.