Det finns ett antal faktorer som spelar in här:
- Nätverkslatens och tur och retur förseningar
- Omkostnader per påstående i PostgreSQL
- Kontextväxlingar och schemaläggningsfördröjningar
COMMIT
kostnader, om för personer som gör en commit per infogning (det är du inte)COPY
-specifika optimeringar för bulklastning
Nätverkslatens
Om servern är avlägsen, kanske du "betalar" ett fast tids-"pris" per påstående på, till exempel, 50 ms (1/20 av en sekund). Eller mycket mer för vissa molnvärdade DB:er. Eftersom nästa infogning inte kan börja förrän den sista har slutförts, betyder detta ditt maximum frekvensen av infogning är 1000/round-trip-latency-in-ms rader per sekund. Vid en latens på 50 ms ("pingtid") är det 20 rader/sekund. Även på en lokal server är denna fördröjning inte noll. Medan COPY
fyller bara TCP:s sändnings- och mottagningsfönster och strömmar rader så snabbt som DB kan skriva dem och nätverket kan överföra dem. Den påverkas inte mycket av latens och kan infoga tusentals rader per sekund på samma nätverkslänk.
Kostnader per rapport i PostgreSQL
Det finns också kostnader för att analysera, planera och utföra ett uttalande i PostgreSQL. Den måste ta lås, öppna relationsfiler, slå upp index etc. COPY
försöker göra allt detta en gång, i början, och fokusera sedan på att ladda rader så snabbt som möjligt.
Kostnader för att byta uppgifter/sammanhang
Det finns ytterligare tidskostnader som betalas på grund av att operativsystemet måste växla mellan postgres som väntar på en rad medan din app förbereder och skickar den, och sedan väntar din app på postgres svar medan postgres bearbetar raden. Varje gång du byter från det ena till det andra slösar du bort lite tid. Mer tid slösas potentiellt bort på att avbryta och återuppta olika lågnivåkärntillstånd när processer går in i och lämnar väntelägen.
Missar COPY-optimeringar
Utöver allt det, COPY
har några optimeringar som den kan använda för vissa typer av belastningar. Om det inte finns någon genererad nyckel och eventuella standardvärden är konstanter, till exempel, kan den förkalkylera dem och förbigå executorn helt och hållet, snabbt ladda in data i tabellen på en lägre nivå som hoppar över en del av PostgreSQL:s normala arbete helt. Om du CREATE TABLE
eller TRUNCATE
i samma transaktion COPY
, den kan göra ännu fler knep för att göra laddningen snabbare genom att kringgå den normala transaktionsbokföring som behövs i en multiklientdatabas.
Trots detta, PostgreSQL:s COPY
kunde fortfarande göra mycket mer för att påskynda saker, saker som den ännu inte vet hur man gör. Det kan automatiskt hoppa över indexuppdateringar och sedan bygga om index om du ändrar mer än en viss del av tabellen. Det kan göra indexuppdateringar i omgångar. Mycket mer.
Avsluta kostnader
En sista sak att tänka på är åtagandekostnader. Det är förmodligen inte ett problem för dig eftersom psycopg2
öppnar som standard en transaktion och inte binder sig förrän du säger till den. Såvida du inte sa åt den att använda autocommit. Men för många DB-drivrutiner är autocommit standard. I sådana fall skulle du göra en commit för varje INSERT
. Det betyder en diskspolning, där servern ser till att den skriver ut all data i minnet till disken och säger åt diskarna att skriva ut sina egna cachar till beständig lagring. Detta kan ta lång tid tid, och varierar mycket beroende på hårdvaran. Min SSD-baserade NVMe BTRFS-laptop kan bara göra 200 fsyncs/sekund, mot 300 000 osynkroniserade skrivningar/sekund. Så det kommer bara att ladda 200 rader/sekund! Vissa servrar kan bara göra 50 fsyncs/sekund. Vissa kan göra 20 000. Så om du måste commit regelbundet, försök att ladda och commit i batcher, gör multi-rad inserts, etc. Eftersom COPY
bara ett åtagande i slutet, åtagandekostnader är försumbara. Men detta betyder också COPY
kan inte återställa från fel halvvägs genom data; det ångrar hela bulklasten.