Skip to content
Everyone knows 4 min read · All formats 5 practice problems ↓

Data lake storage: formats and layout

CSV, JSON, Avro, ORC and Parquet compared, compression, and how to lay out folders in a data lake.

You will learn

  • How CSV, JSON, Avro, ORC and Parquet differ, and when each fits
  • Why columnar formats are faster for analytics
  • How compression codecs trade CPU for size
  • How to lay out folders and zones in a data lake

Read first

Comfortable with these? Read on.

TL;DR A data lake is files in object storage, so the file format decides how fast everything is. Row formats (CSV, JSON, Avro) are good for landing and exchanging data; columnar formats (Parquet, ORC) are good for analytics because queries read only the columns and row groups they need. Lay the lake out in zones (raw, cleaned, curated), partition by a low-cardinality column queries filter on, and keep files large.

What a data lake is

A data lake stores data as files in cheap object storage (Amazon S3, Azure Data Lake Storage, Google Cloud Storage) and lets many engines read it: Spark, Trino, Flink, DuckDB, warehouses. Storage is separated from compute, so you pay for compute only while queries run. The price is that the lake has no database to enforce anything: formats, layout and conventions do that job, and table formats like Delta and Iceberg add transactions on top.

The formats compared

FormatLayoutSchemaSplittableBest for
CSVRow, textNone (all strings)Yes, if uncompressed or with a splittable codecExchange with humans and legacy systems
JSON (Lines)Row, textSelf-describing per recordYes for JSON LinesAPIs, logs, nested data on landing
AvroRow, binaryEmbedded, with evolution rulesYesKafka messages, write-heavy pipelines, schema evolution
ORCColumnar, binaryEmbeddedYesAnalytics in Hive-era stacks
ParquetParquet: A columnar file format: values of each column are stored together, so queries read only the columns they need. Learn more →Columnar, binaryEmbeddedYesAnalytics; the default under Delta, Iceberg and Hudi

Why columnar wins for analytics

Input

Row layout (CSV, Avro)
1, Pune, 250
2, Delhi, 900
3, Pune, 120

Output

Columnar layout (Parquet, ORC)
id: 1, 2, 3
city: Pune, Delhi, Pune
amount: 250, 900, 120

The same three rows, two layouts

  • Column pruningcolumn pruning: Reading only the columns a query uses, which columnar files like Parquet make possible. Learn more →: SELECT sum(amount) reads one column out of fifty, not all of them.
  • Better compression: similar values sit together, so dictionary and run-length encoding shrink them a lot (a city column with 100 distinct values compresses to almost nothing).
  • Skipping: each row grouprow group: A block of rows inside a Parquet file. Its min and max statistics let readers skip blocks that cannot match a filter. Learn more → stores min/max statistics per column, so WHERE amount > 1000 skips row groups whose max is lower.
  • The cost: writing is heavier, and reading one whole record touches every column chunk. That is why landing and streaming often use row formats.

Compression codecs

CodecSizeSpeedTypical use
SnappyGoodVery fastSpark's Parquet default
ZSTDBetterFastIncreasingly the default for Parquet and Iceberg; a good general choice
GZIPBetterSlowArchives; avoid on CSV, because a .gz file cannot be split, so one tasktask: The work for one partition in one stage, run on one CPU core. Learn more → reads it all
LZ4GoodFastestHot data, shuffleshuffle: Moving rows between machines so that all rows with the same key end up together. Needed by joins, groupBy and sorting, and usually the most expensive step of a job. Learn more → files

Inside Parquet, compression applies per column chunk, so every codec is splittable there. Outside Parquet, a single 10 GB .csv.gz file is read by one task, which is a common reason a job has one task running for an hour.

Zones: raw to curated

  1. Raw / landing

    Exactly as received: CSV, JSON, Avro. Append-only, never edited, kept for replay and audit.

  2. Cleaned / bronze-silver

    Parsed, typed, de-duplicated, in Parquet or a table format.

  3. Curated / gold

    Modelled for consumers: facts, dimensions, aggregates.

This is the same idea as the medallion architecture. Keeping the raw zone means any bug downstream can be fixed by reprocessing from the original files.

Folder layout

A typical layout

s3://acme-lake/
  raw/orders/ingest_date=2025-03-01/part-0000.json.gz
  silver/orders/order_date=2025-03-01/part-00000.zstd.parquet
  gold/daily_revenue/...
  • Hive-style folders (column=value) let engines prune partitions from the path.
  • Partition by a low-cardinalitycardinality: How many distinct values a column has. User ids are high cardinality; country codes are low. Learn more → column that queries filter on, usually a date. Partitions should hold at least about 1 GB.
  • Aim for files of 128 MB to 1 GB.
  • Separate buckets or prefixes per zone make access control simple: few people can write to raw, many can read gold.
  • Never rely on listing folders to know what a table contains: concurrent writers make that unsafe. That is the problem table formats solve with a transaction logtransaction log: The ordered list of commits that defines which files make up a Delta table at each version. Learn more →.

Common mistakes

Analytics on CSV or JSON

Every query reads every column as text. Convert to Parquet once.

Large gzip CSV files

Not splittable: one task per file.

Partitioning by a high-cardinality column

Millions of tiny folders and files.

No raw zone

A parsing bug cannot be fixed by reprocessing because the original data is gone.

Key takeaways

  • Row formats suit landing and exchange; columnar formats suit analytics.
  • Parquet gives column pruning, compression and row-group skipping.
  • Avoid big gzip text files: they cannot be split.
  • Use zones, date partitions and large files; add a table format for transactions.

Check yourself

3 questions

1. A query sums one column of a 50-column table. Which format reads the least data?

Show the answer

Parquet. Columnar formats read only the needed column.

2. Why is a 10 GB .csv.gz file slow to read in Spark?

Show the answer

gzip is not splittable, so one task reads the whole file. Without split points, one task must decompress from the start.

3. What is the raw zone for?

Show the answer

Keeping data exactly as received so it can be reprocessed. It is the replayable source of truth.

Practice it

Interview problems that use this: write the PySpark, run it, and get graded on hidden tests.

Solve: Inventory a Partitioned Folder →

Keep going

Up next · lesson 3 of 26 · 4 min read
Parquet and columnar storage
Row groups, column chunks and statistics: why columnar files let engines skip most of the data.

Related lessons

Previous: Lake, warehouse, lakehouse

Primary sources: Apache Parquet · Apache Avro · Spark data sources