Tillbaka till bloggen
11 min läsning

CSV vs. Parquet: vilket filformat ska du välja för moderna dataplattformar?

Isak La Fleur EngdahlAv Isak La Fleur Engdahl
En CSV-filikon och en Parquet-logotyp på var sin sida om ett 'VS', ett tema för filformatsjämförelse

I nästan varje migrerings- och MDM-uppdrag jag arbetar med dyker samma fråga upp förr eller senare:

Ska vi lagra den här datan som CSV eller Parquet?

CSV har varit det universella utbytesformatet i decennier – nästan varje affärssystem kan exportera och importera det. Parquet har blivit standard för moderna analysplattformar: Spark, Databricks, Snowflake, DuckDB och dataladorna under dem.

Det ärliga svaret är att inget av formaten helt enkelt är "bättre". De löser olika problem. Så istället för att resonera utifrån principer körde jag båda formaten genom samma uppgifter på min egen laptop och lät siffrorna avgöra. Nedan följer de uppmätta resultaten, plus ett praktiskt ramverk för när du ska välja vilket.

Vad är CSV?

CSV (Comma-Separated Values) är ett textformat där varje rad är en post och värdena separeras med ett avgränsningstecken:

CustomerId,Name,Country,Revenue
1001,Acme Ltd,Sweden,15000
1002,Globex,Denmark,24000

Styrkor: läsbart för människor, trivialt att generera, stöds överallt, idealiskt för att flytta data mellan system.

Svagheter: stora filer, ingen typinformation, ingen komprimering som standard, och varje läsning måste scanna hela rader även när du bara behöver en kolumn.

Vad är Parquet?

Parquet är ett kolumnärt lagringsformat byggt för analys. Istället för att lagra data rad för rad lagras värden kolumn för kolumn. Samma tabell, internt:

CustomerId:  1001, 1002
Name:        Acme Ltd, Globex
Country:     Sweden, Denmark
Revenue:     15000, 24000

Att gruppera varje kolumns värden tillsammans låser upp tre saker CSV inte klarar: aggressiv komprimering (liknande värden ligger bredvid varandra), ordboksencoding för upprepade värden, och möjligheten att läsa en enda kolumn utan att röra resten av filen. Det bäddar dessutom in schemat – kolumnnamn och typer – direkt i filen.

Testet

Jag ville ha uppmätta siffror, inte folktro, så jag körde båda formaten genom samma uppgifter på samma maskin:

  • Testrigg: Apple MacBook Air (M4), 16 GB RAM, Python 3.14, pandas 3.0.3, pyarrow 24.0.0. Tidtagning via timeit, median av upp till 7 körningar.
  • Litet / interoperabilitetsfall: det klassiska Iris-datasetet – 5 kolumner, 150 rader. Den sortens lilla fil som vräker ut sig ur ERP- och Excel-exporter.
  • Stort / analysfall: ett verkligt bedrägeridetekteringsdataset – 18 kolumner, 5 miljoner rader, 754 MB som rå CSV. En realistisk blandning: kategorivärden med låg kardinalitet (transaktionstyp, handelskategori, plats) bredvid nycklar med hög kardinalitet (kontonummer, IP-adresser, enhets-hashar).

För varje dataset jämförde jag filstorlek på disk (CSV, gzippad CSV, Parquet) och de operationer som spelar roll: en full läsning, en skrivning, läsning av en enda kolumn, en läs-och-filtrera-scanning, och en grupperad aggregering. På den stora filen svepte jag dessutom igenom varje Parquet-komprimeringscodec.

Resultat: den lilla filen (Iris)

CSV Parquet
Storlek på disk 3,8 KB 4,6 KB
Full läsning 0,22 ms 0,47 ms
Skrivning 0,27 ms 0,38 ms
Läs 1 kolumn 0,16 ms 0,32 ms
Läs + filtrera 0,33 ms 0,56 ms

Läs den tabellen två gånger, för den är motsatsen till vad marknadsföringen antyder: för en liten fil vinner CSV på varje rad. Parquet-filen är faktiskt större (dess footer, schema och metadata per kolumn kostar mer än själva datan vid den här storleken), och varje operation är ~2x långsammare eftersom det finns kolumnstruktur att sätta upp innan något arbete kan börja.

Det här är interoperabilitetens kärnområde. En affärsanvändare kan öppna CSV-filen i Excel och granska den på sekunder; Parquet-filen kräver verktyg. Vid 150 rader är det kolumnära maskineriet ren overhead.

Resultat: den stora filen (5M bedrägeritransaktioner)

Här ställs CSV mot Parquets standardcodec (snappy):

CSV Parquet (snappy) Parquet-fördel
Storlek på disk 754,1 MB 318,7 MB 2,4x mindre
Full läsning 8,32 s 133 ms ~63x snabbare
Skrivning 14,13 s 1,94 s ~7x snabbare
Läs 1 kolumn 1,56 s 17 ms ~92x snabbare
Läs + filtrera 7,96 s 269 ms ~30x snabbare
Gruppera 1,86 s 129 ms ~14x snabbare

Nu vänds bilden helt. Toppresultatet är att läsa en enda kolumn – ~92x snabbare, eftersom Parquet bara rör den kolumnen på disk medan CSV måste parsa alla 18. Fulla läsningar landar på ~63x, filtrerade scanningar ~30x. Till och med gzippad CSV (273 MB) är större än snappy-Parquet, och den måste dessutom dekomprimeras och parsas helt innan du kan ställa en fråga mot den.

En notering om den där 2,4x-kvoten: det här datasetet är ärligt om sina gränser. Dess största kolumner är nycklar med hög kardinalitet – kontonummer, IP-adresser, enhets-hashar, transaktions-ID – som knappt komprimeras, eftersom nästan varje värde är unikt. Parquets ordboksencoding livnär sig på repetition, så på tabeller som domineras av kategorivärden med låg kardinalitet (statusar, landskoder, produkttyper) ser du betydligt mer – 10x och uppåt. Vilken codec du väljer flyttar den här siffran också, vilket var nästa sak jag mätte.

Parquet är inte ett format: komprimeringssvepet

"Parquet" är egentligen en container plus ett val av komprimeringscodec, och det valet är inte kosmetiskt. Jag skrev ut samma frame med 5 miljoner rader med varje codec som pandas/pyarrow stödjer och mätte resultatet:

Lagring på disk (rå CSV är 754,1 MB):

Format Storlek vs CSV
CSV (gzip) 273,0 MB 2,8x
Parquet (okomprimerad) 550,0 MB 1,4x
Parquet (snappy) 318,7 MB 2,4x
Parquet (lz4) 324,0 MB 2,3x
Parquet (zstd) 207,2 MB 3,6x
Parquet (gzip) 213,8 MB 3,5x
Parquet (brotli) 202,8 MB 3,7x

Skriv- och lästider:

Format Skrivning Full läsning 1 kolumn Filter
Parquet (okomprimerad) 1,36 s 135 ms 11 ms 293 ms
Parquet (snappy) 1,94 s 133 ms 17 ms 269 ms
Parquet (lz4) 1,96 s 123 ms 13 ms 257 ms
Parquet (zstd) 2,13 s 162 ms 21 ms 292 ms
Parquet (gzip) 65,74 s 260 ms 43 ms 389 ms
Parquet (brotli) 13,86 s 285 ms 50 ms 411 ms

Några saker sticker ut:

  • Varje codec krossar CSV – till och med okomprimerad Parquet läser ~60x snabbare än CSV och en enda kolumn ~140x snabbare. Den vinsten kommer från den kolumnära, binära, schemamedvetna layouten, inte från komprimeringen. Komprimering är en bonus ovanpå.
  • zstd är den gyllene medelvägen. Den når 3,6x komprimering – så gott som lika med den bästa – men skriver på ~2 s och läser nästan lika snabbt som snappy. Bäst lagring-per-sekund med marginal.
  • snappy och lz4 är hastighetskungarna. Snabbast läsning och skrivning, men de offrar komprimering (~2,4x). Rätt standard när skrivgenomströmning eller låg CPU väger tyngre än disk.
  • Parquet + gzip är en fälla. Det tog 65 sekunder att skriva – 30x långsammare än zstd – för sämre komprimering än zstd. Bekant namn, fel verktyg för jobbet.
  • brotli vinner på storlek (3,7x, den minsta filen) men du betalar i skriv- och lästid. Värt det för kall arkivdata du skriver en gång och sällan rör.

Den praktiska standarden: zstd för lagring du behåller, snappy/lz4 för het mellandata du skriver om ofta. Skillnaden i läshastighet mellan codecar håller sig inom ~2x, så det är lagring och skrivkostnad som faktiskt avgör valet.

Varför brytpunkten uppstår

De två resultattabellerna berättar en historia: Parquets fördel skalar med datamängden, och under en viss tröskel är den en nettokostnad.

  • Kolumnär layout har fast overhead. Schema, footer och metadata per kolumn är ungefär konstanta oavsett om filen har 150 rader eller 5 miljoner. På en liten fil dominerar den overheaden; på en stor fil är den en avrundningsfel.
  • Kolumnprojektion är Parquets superkraft. ~92x-resultatet för en enda kolumn är hela anledningen till att analysmotorer älskar det. SELECT revenue FROM sales WHERE country = 'Sweden' mot en Parquet-tabell med 100 kolumner läser ungefär två kolumner från disk. Samma fråga mot CSV läser alla 100.
  • Apple Silicon smickrar båda. M4:ans snabba SSD och höga minnesbandbredd håller de absoluta tiderna låga, men de ändrar inte resultatets form – brytpunkten är strukturell, inte hårdvaruspecifik.

Schema: den tysta fördelen

Bortom hastighet och storlek löser Parquet ett problem som CSV skapar. Det här är en giltig CSV:

1001,15000
1002,24000

Är den första kolumnen en sträng eller ett heltal? Är revenue ett decimaltal? Vad betyder varje kolumn? CSV kan inte svara – det importerande systemet måste veta. Parquet bäddar in svaret (CustomerId: INT64, Revenue: DECIMAL(18,2), Country: STRING), vilket betyder färre importfel, starkare validering och enklare styrning. I migreringsarbete, där en feltypad nyckelkolumn tyst kan korrumpera en laddning, förtjänar det självbeskrivande schemat sin plats.

Vad detta betyder för migreringar

Under ERP-migreringar finns CSV fortfarande överallt – med rätta. Nästan varje plattform talar det: SAP, Microsoft Dynamics, Oracle, Infor, Salesforce. CSV är lingua franca mellan system:

Legacysystem → Extrahera CSV → Transformera → Validera → Mål-ERP

En affärsanvändare kan öppna extraktet i Excel och ögna igenom det direkt. Det är mycket svårare med Parquet, och resultaten för små filer ovan visar att det inte finns något att vinna på att tvinga fram det. För utbyteslagret väger CSV:s enkelhet och universalitet oftast tyngre än effektivitet.

Vad detta betyder för analys

Analysarbetslaster har motsatt profil. Tänk dig en datalada med miljarder transaktioner och dagliga rapportfrågor. På CSV betyder det långa laddningar, dyra scanningar och en brant molnlagringsnota. På Parquet betyder det snabbare frågor, lägre lagringskostnad och frågor som bara läser de kolumner de behöver – vilket är precis varför moderna analysplattformar lagrar data som Parquet internt:

ERP → Datalada → Parquet → Analys & AI

DuckDB-asterisken

Det är värt att tempera analysfallet med en reservation som DuckDB-teamet fört fram väl: moderna motorer läser CSV mycket snabbare än ryktet antyder. De parallelliserar parsning, autodetekterar scheman och scannar text effektivt. För en engångsläsning eller ett mindre dataset kan CSV vara fullt tillräckligt – mina Iris-resultat är en miniatyrversion av samma poäng.

Principen under båda fynden är densamma:

Det bästa formatet beror på hur datan ska användas. Läses den en gång är CSV bra. Frågas den upprepade gånger vinner Parquet – och ju större datan är, desto mer vinner den.

En praktisk rekommendation

I de flesta organisationer är rätt svar inte CSV eller Parquet. Det är båda, var och en där det lönar sig:

Källsystem → CSV-utbyteslager → Transformation → Parquet-lagring → Analys & AI
  • Använd CSV för att flytta data mellan system, ERP-import/-export, för att stötta affärsanvändare, och överallt där maximal kompatibilitet är viktig.
  • Använd Parquet för dataladuugor, analysarbetslaster, stora eller upprepat frågade dataset, och allt som matar Spark, Databricks, DuckDB eller en AI-pipeline.

CSV är interoperabilitetens kung. Parquet är analysens kung. Den användbara frågan är aldrig "vilket format är bättre?" – den är "vad försöker vi faktiskt göra med den här datan?"

Reproducera det själv

Vill du köra samma jämförelse på din egen maskin? Hämta skriptet – det är exakt koden bakom den här artikeln:

csv-vs-parquet-benchmark.py

Det kör den lilla Iris-jämförelsen och hela Parquet-komprimeringssvepet. Peka --large mot vilken CSV du vill (det faller tillbaka på ett syntetiskt substitut om du saknar en), och --sample-rows för en snabbare körning:

pip install numpy pandas pyarrow

# Direkt ur kartongen (syntetiskt stort dataset om du saknar ett):
python csv-vs-parquet-benchmark.py

# Din egen stora CSV, nedsamplad till 1M rader för fart:
python csv-vs-parquet-benchmark.py --large transactions.csv --sample-rows 1_000_000

De absoluta siffrorna skiljer sig på din hårdvara och data – både variation mellan körningar och, framför allt, dina kolumners kardinalitet spelar in – men formen håller: CSV för små filer och interoperabilitet, Parquet för stora, upprepat frågade data, och zstd som en vettig standardcodec.

Nyfiken på hur brytpunkten ser ut på dina egna dataset? Hör av dig – eller kör bara skriptet och se dina faktiska tider.