Skip to content
Storage Engines: OLTP (Row-Oriented) vs OLAP (Columnar/Parquet/ClickHouse/Apache Arrow In-Memory Engine)

Storage Engines: OLTP (Row-Oriented) vs OLAP (Columnar/Parquet/ClickHouse/Apache Arrow In-Memory Engine)

What it is

A storage engine is the database layer that maps logical rows, columns, and indexes to pages, files, buffers, and durable logs. It determines the access pattern a database optimizes: row-oriented B-tree engines favor OLTP point operations, log-structured merge-tree engines favor append-heavy writes, and columnar engines favor scans and aggregations.

How it works

A row-oriented engine such as InnoDB stores an entire row in a page and organizes secondary indexes into B-trees. A transaction records changes in a write-ahead log, updates data pages, and recovers by replaying the log after a crash. A heap-plus-index design also lets a secondary index identify rows without duplicating the complete row.

A log-structured merge-tree engine such as RocksDB accepts writes in a memtable and flushes immutable sorted tables called SSTables. Reads search the memtable and relevant SSTables, using per-table filters to skip files that cannot contain a key. Compaction merges files to control file count and read amplification. Cassandra, HBase, and many other systems use LSM families for write-heavy workloads.

Columnar OLAP engines store values by column, compress repeated values, and process only the columns referenced by a query. Parquet provides a file format for this layout, while ClickHouse is a server engine that executes queries over columnar parts. OLTP row storage remains a better fit when a transaction updates a few attributes in a small record.

    flowchart LR
    Request --> Layout{Access pattern}
    Layout --> Point[OLTP point operation]
    Point --> Row[Row and B-tree pages]
    Row --> WAL[WAL]
    WAL --> Pages[Durable data pages]
    Layout --> Write[Append-heavy write]
    Write --> Memtable[Memtable]
    Memtable --> SSTables[SSTables]
    SSTables --> Compact[Compaction]
    Layout --> Analytics[OLAP scan]
    Analytics --> Columns[Column segments]
    Columns --> Predicate[Pruning and vectorized aggregation]
  
oltp_row_engine:
  examples: [InnoDB, PostgreSQL heap]
  layout: row_and_index_pages
  write_path: wal_then_page_update
  recovery: wal_replay
olap_column_engine:
  examples: [ClickHouse]
  format: columnar_parts
  write_path: append_and_merge
  read_path: predicate_pushdown_and_vectorized_aggregation
lsm_engine:
  examples: [RocksDB, LevelDB, Cassandra, HBase]
  write_path: memtable_to_sstable
  maintenance: compaction
  read_path: memtable_and_sstable_search

Complexity

OperationB-tree page engineLSM-tree engineColumnar OLAP engine
Point lookup by keyO(log n) after warm pagesDepends on the workload and SSTables searched; each relevant SSTable is typically O(log s)Usually not the primary access pattern
Range scanO(log n + k) pages or index entriesDepends on overlapping SSTables and filters; output work is O(k) after per-table search and read costsO(Σ selected column values)
Point updateO(log n) index work plus page writeLookup cost depends on the workload and SSTables searched, then append or merge work remainsHigher when a column segment must be rewritten
Bulk append or loadRepeated page and index maintenanceSequential writes plus eventual compactionEfficient column-wise encoding and compression
Memory and diskBuffers, pages, indexes, and WALMemtable, WAL, SSTables, filters, and compactionCompressed column segments plus query intermediates
AmplificationRandom page and index writesRead and write amplification; compaction I/OColumn pruning and vectorized execution trade memory for throughput

Here, n is the number of entries in a B-tree, m is the number of SSTables searched, s is the number of entries in one SSTable, and k is the number of matching entries. LSM read cost also depends on key distribution, bloom-filter effectiveness, level structure, compaction, cache behavior, and overlap. The constants also depend on page size, compression, and hardware; the table describes asymptotic work rather than a latency guarantee.

When to use

  • You need transactional point reads and writes with strong constraints and durable commits.
  • You ingest events or measurements where append throughput matters more than in-place updates.
  • You run analytical scans that read a small subset of columns over many rows.
  • You understand the backup, compaction, and recovery behavior of the engine you choose.

Alternatives

  • Heap plus B-tree indexes — flexible row updates and familiar relational behavior, with page and index maintenance costs.
  • LSM-tree storage — high write throughput and efficient sequential persistence, with read amplification and compaction overhead.
  • Columnar storage — compression, pruning, and aggregation, with less natural behavior for small transactional updates.
  • In-memory storage — low latency and simple access paths, with capacity and durability requirements that need explicit design.

Related