Problem
Analytical queries typically aggregate a few columns across billions of rows. Row-oriented storage forces the engine to read every column of every row, wasting I/O. Mixed value types in a row also compress poorly.
Also known as: column-store, parquet-layout
Store each column contiguously so analytical queries read and compress only the columns they need.
Analytical queries typically aggregate a few columns across billions of rows. Row-oriented storage forces the engine to read every column of every row, wasting I/O. Mixed value types in a row also compress poorly.
Columnar formats such as Parquet and ORC, and engines such as ClickHouse, Redshift and BigQuery, store values column by column. A query summing revenue by region reads two columns instead of fifty.
Same table, two layouts.
Row layout: [id1,US,9.5,...] [id2,EU,4.0,...] [id3,US,7.2,...]
Column layout: id: [id1,id2,id3]
region: [US,EU,US] -> dictionary + RLE
amount: [9.5,4.0,7.2]
SELECT region, SUM(amount) FROM sales GROUP BY region;
-- reads only region and amount blocks
Each block stores min and max values, so a filter on a date range skips blocks entirely.
Encoding choices are chosen per column. A low-cardinality country column compresses to a dictionary of ids further squeezed by run-length encoding, a monotonic timestamp column compresses with delta encoding, and a sorted column benefits most because equal values sit together. Engines process data in vectors of a thousand or so values, keeping loops tight and CPU caches warm. Many warehouses also use table partitioning by date and clustering by a secondary key, so most queries read only a small part of the files.
Medium - lower storage per row but compute-heavy scans
Medium - ingestion batching and compaction
Medium - sort keys and partitioning choices matter
Trickle inserts create many tiny files and slow queries
Poor sort key choice defeats block skipping
Using it for transactional updates causes write contention