Library
8 min read

OLTP vs OLAP

Published

A database serves two very different kinds of question. "Place order 1004 for Asha" and "show me order 1004" are transactions: small, specific, thousands per second, and they need an answer in milliseconds. "What was our revenue by city, per hour, for the last 90 days?" is an analytical question: it reads hundreds of millions of rows, a few times an hour, and a few seconds is fine.

OLTP (online transaction processing) is the first kind. OLAP (online analytical processing) is the second. Trying to run both on the same database is one of the most common ways a growing company takes down its own checkout.

Your answer

The finance team runs a revenue report directly on the production Postgres at 11 am. Checkout latency jumps from 40 ms to 4 seconds while it runs. Why would a read only report slow down writes? What would you change?

The layout on disk is the whole story#

Disks and SSDs read in pages (8 KB in Postgres), never single values. So what matters is which values sit next to each other. A row store keeps each order's fields together. A column store keeps each column's values together. Run the three queries on both layouts.

Same data, two layouts on disk
SELECT SUM(amount) FROM orders
order_iduser_idcityitemamountplaced_at
100142PuneBiryani32018:02
100277DelhiThali21018:03
100342PuneDosa14018:05
100419KochiAppam18018:06
100588DelhiMomos16018:08
100623PuneMisal12018:09
100777KochiBiryani34018:11
100851DelhiChaat9018:12
Pages read: 4 of 4Other layout: 2Solid: values the query needs. Dashed: read anyway, because they share a page.
Every page is read, because each one mixes the amount you want with user ids, items and timestamps you do not. Scale this to a billion orders and you read the whole table to add up one column.
Row store: 2 orders per page. Column store: 4 values of one column per page. Real pages hold thousands of rows; the ratio is the point.

That is the entire reason OLTP and OLAP systems exist separately. Fetching or updating one order wants the order's fields together. Summing one column over a billion orders wants that column's values together, and wants to skip the other 30 columns entirely. A column of similar values (city names, amounts) also compresses extremely well, often five to ten times, and is processed with vectorised CPU instructions a batch at a time.

Total revenue over 500 million orders
500 million
30
6 x
Row store reads every column of every row500M x 30 x 8 B = 120.0 GB
Column store reads one compressed column500M x 8 B / 6 = 0.7 GB
At about 2 GB/s of disk60 s vs 0.33 s
180x
less data read by the column store

Assumes 8 bytes per value and a single disk, which flatters neither system. Real OLAP engines also run the scan across many cores and machines, which is why BigQuery answers this in a couple of seconds.

The two worlds side by side#

OLTPOLAP
Typical queryRead or write a few rows by keyScan and aggregate millions or billions of rows
VolumeThousands to millions of queries a secondA few to a few thousand a minute
Latency targetMillisecondsSeconds is fine
DataCurrent stateHistory, often years
SchemaNormalised, many tablesDenormalised, wide fact tables (star schema)
StorageRow orientedColumn oriented, compressed
WritesConstant small updatesBulk loads or appends, rarely updates
ExamplesPostgres, MySQL, DynamoDB, MongoDBBigQuery, Snowflake, Redshift, ClickHouse, DuckDB

Why the report slowed checkout#

A big analytical query on the OLTP primary reads the whole table, evicts the hot pages that checkout depends on from the buffer cache, saturates disk and CPU, and in some databases holds locks or prevents cleanup (a long running Postgres transaction stops vacuum from removing dead rows). The fix is to give analytics its own copy:

Options, from simplest to most capable:

  • A read replica for reports. Isolates the primary's CPU and cache, but it is still a row store, so big scans are still slow.
  • Nightly ETL (extract, transform, load) into a warehouse. Simple and cheap, but data is up to a day old.
  • Change data capture (CDC) streaming every change through Kafka into the warehouse, so analytics are a minute or so behind. This is the modern default.

ELT is the now common variant where raw data is loaded first and transformed inside the warehouse with SQL (dbt is the usual tool). And HTAP systems (TiDB, SingleStore, AlloyDB's columnar engine) try to serve both workloads from one system by keeping a columnar copy beside the rows, useful when you cannot afford a pipeline but need fresh analytics.

ChoiceWhat you gainWhat you payPick it when
OLTP database (row store)Fast point reads and writes, transactions, constraints, indexes on keys.Large scans and aggregates are slow and hurt everything else on the box.Running the product: orders, users, payments, inventory.
OLAP warehouse (column store)Scans and aggregates over billions of rows in seconds, cheap storage via compression.Poor at single row lookups and updates, data arrives with some delay, another system to feed.Dashboards, reporting, analytics, feature generation for ML.

In the interview#

Interviewer
Ops wants a live dashboard of orders per city per minute. Where does that query run?
The wrong answer is "a GROUP BY on the orders table".
You
Not on the primary. I would stream changes out with CDC, Debezium reading the write ahead log into Kafka, and load them into a column store like ClickHouse, possibly with a materialised view that keeps per city per minute counts up to date as rows arrive. The dashboard queries that and is a few seconds behind live. If we only needed daily reports, a nightly load into BigQuery would be simpler and cheaper.
Pipeline plus a pre-aggregation, and a cheaper alternative tied to the freshness need.
OLTP or OLAP?
Show a user their last 10 orders
Average delivery time by restaurant for last quarter
Decrement stock when an item is bought
Daily active users for the last two years

Checkpoint#

Checkpoint

1. Why is a column store faster for SUM(amount) over all orders?

2. Why can a read only report slow down writes on the primary?

3. What does change data capture do?

Worth memorising
  • OLTP: many small reads and writes, row store, milliseconds.
  • OLAP: few huge scans and aggregates, column store, seconds.
  • Column stores read only needed columns and compress 5 to 10x.
  • Never run big reports on the primary. Replica, ETL, or CDC to a warehouse.
  • CDC: Debezium + Kafka into ClickHouse, BigQuery or Snowflake.
Say this in 60 seconds

OLTP serves the application: many small reads and writes by key, millisecond latency, current state, normalised schemas, and row oriented storage like Postgres or MySQL, because fetching or updating one record wants its fields together on one page. OLAP answers analytical questions over history: few queries that scan and aggregate millions or billions of rows, where seconds are fine, using column stores like BigQuery, Snowflake, Redshift or ClickHouse, which read only the needed columns, compress them heavily and process them in vectorised batches. Running analytics on the production primary competes for disk, CPU and cache and can stall checkout, so I give analytics its own copy: a read replica as a stopgap, nightly ETL when a day of delay is fine, or change data capture through Kafka into a warehouse when it needs to be within a minute.