Der er en række faktorer, der spiller ind her:
- Netværksforsinkelse og tur-retur-forsinkelser
- Per-state overhead i PostgreSQL
- Kontekstskift og planlægningsforsinkelser
COMMIT
omkostninger, hvis for folk, der laver én commit pr. indstik (det er du ikke)COPY
-specifikke optimeringer til bulk loading
Netværksforsinkelse
Hvis serveren er ekstern, "betaler" du muligvis en fast tids-"pris" pr. udsagn på f.eks. 50 ms (1/20 af et sekund). Eller meget mere for nogle cloud-hostede DB'er. Da den næste indsættelse ikke kan begynde, før den sidste er fuldført, betyder det din maksimum hastigheden af indsættelser er 1000/tur-retur-latens-i-ms rækker pr. sekund. Ved en latenstid på 50 ms ("pingtid") er det 20 rækker/sekund. Selv på en lokal server er denne forsinkelse ikke nul. Hvorimod COPY
udfylder bare TCP sende og modtage vinduer, og streamer rækker så hurtigt som DB kan skrive dem og netværket kan overføre dem. Den påvirkes ikke meget af latens og indsætter muligvis tusindvis af rækker pr. sekund på det samme netværkslink.
Udgifter pr. opgørelse i PostgreSQL
Der er også omkostninger til at parse, planlægge og udføre en erklæring i PostgreSQL. Det skal tage låse, åbne relationsfiler, slå indeks op osv. COPY
forsøger at gøre alt dette én gang, i starten, og derefter fokusere på at indlæse rækker så hurtigt som muligt.
Omkostninger til skift af opgaver/kontekst
Der betales yderligere tidsomkostninger på grund af, at styresystemet skal skifte mellem postgres, der venter på en række, mens din app forbereder og sender den, og så venter din app på postgres' svar, mens postgres behandler rækken. Hver gang du skifter fra den ene til den anden, spilder du lidt tid. Mere tid er potentielt spildt på at suspendere og genoptage forskellige lavniveau-kernetilstande, når processer går ind og forlader ventetilstande.
Glip af COPY-optimeringer
Oven i alt det, COPY
har nogle optimeringer, den kan bruge til nogle slags belastninger. Hvis der ikke er nogen genereret nøgle, og eventuelle standardværdier er konstanter for eksempel, kan den forudberegne dem og omgå eksekveren fuldstændigt, hurtigt indlæse data i tabellen på et lavere niveau, der springer en del af PostgreSQL's normale arbejde helt over. Hvis du CREATE TABLE
eller TRUNCATE
i den samme transaktion COPY
, kan den gøre endnu flere tricks til at gøre indlæsningen hurtigere ved at omgå den normale transaktionsbogføring, der er nødvendig i en multiklientdatabase.
På trods af dette, PostgreSQL's COPY
kunne stadig gøre meget mere for at fremskynde tingene, ting som den endnu ikke ved hvordan man gør. Det kan automatisk springe indeksopdateringer over og derefter genopbygge indekser, hvis du ændrer mere end en bestemt del af tabellen. Det kunne lave indeksopdateringer i batches. Meget mere.
Forpligte omkostninger
En sidste ting at overveje er at forpligte omkostninger. Det er sandsynligvis ikke et problem for dig, fordi psycopg2
standard til at åbne en transaktion og ikke forpligte sig, før du fortæller det. Medmindre du har bedt den bruge autocommit. Men for mange DB-drivere er autocommit standarden. I sådanne tilfælde ville du foretage en commit for hver INSERT
. Det betyder én diskflush, hvor serveren sørger for, at den skriver alle data i hukommelsen ud på disken og fortæller diskene, at de skal skrive deres egne caches ud til vedvarende lagring. Dette kan tage lang tid tid, og varierer meget afhængigt af hardwaren. Min SSD-baserede NVMe BTRFS bærbare computer kan kun udføre 200 fsyncs/sekund, mod 300.000 ikke-synkroniserede skrivninger/sekund. Så det vil kun indlæse 200 rækker/sekund! Nogle servere kan kun udføre 50 fsyncs/sekund. Nogle kan klare 20.000. Så hvis du skal commit regelmæssigt, så prøv at indlæse og commit i batches, lav multi-row inserts osv. Fordi COPY
kun én commit til sidst, commit omkostninger er ubetydelige. Men dette betyder også COPY
kan ikke komme sig efter fejl halvvejs gennem dataene; det fortryder hele bulklasten.