Lesson 0002 · Storage engine

WiredTiger: how documents are stored

MongoDB's default engine gives you MVCC, durability, and space reuse — but the InnoDB way, not the Postgres way. A point-in-time snapshot per operation, a write-ahead journal plus checkpoints, and freed space it reuses on its own. No VACUUM.

~10 minWarm-up + retrieval quiz + 3-way contrastCheat sheet: here

Warm-up — recall lesson 0001 first (cold, from memory)

A write that changes many fields and nested arrays of one document is atomic:

Single-document writes are atomic on the document boundary — that's the payoff of embedding, and the reason you rarely need a transaction.

The core principle that makes embedding the default modeling move is:

"Data accessed together is stored together" — model to the access pattern, which turns a join into a one-document read.

The pressure that most directly forces referencing over embedding is:

A document caps at 16 MB, so an ever-growing embedded array eventually breaks — large/unbounded/independent data is the signal to reference.

Lesson 0001 was about the shape of data — the document. This one drops a level: how MongoDB actually puts documents on disk and keeps them safe. You have two sharp reference points already. InnoDB: a B+tree in a buffer pool, updates roughly in place, old versions in the undo log, a redo log for durability. Postgres: an append-only heap, every version inline, a background VACUUM to reclaim dead tuples, a WAL for durability. The question this lesson answers: where does MongoDB's WiredTiger engine sit between them? The short answer — and the thing to hold onto — is much closer to InnoDB.

The one idea

WiredTiger is the default storage engine, and it stores each collection and index as a B-tree — internal pages hold keys and pointers, leaf pages hold the records in sorted key order. MongoDB Manual: "The WiredTiger storage engine is the default storage engine." · WiredTiger Architecture Guide: tables are represented as a B-Tree; internal pages store keys and references, leaf pages store keys and values in sorted order. That alone rhymes with InnoDB (a B+tree) and stands apart from the Postgres heap. But the deeper resemblance is in the three things a storage engine has to solve: concurrency, space, and durability.

Concurrency: MVCC by snapshot, at document granularity

Like both engines you know, WiredTiger uses MVCC: at the start of an operation it hands that operation a point-in-time snapshot — a consistent view of the data as of that instant. MongoDB Manual: "WiredTiger uses MultiVersion Concurrency Control (MVCC). At the start of an operation, WiredTiger provides a point-in-time snapshot of the data to the operation. A snapshot presents a consistent view of the in-memory data." This is the same shape as the Postgres snapshot you met in Postgres 0005 and the InnoDB consistent read from MySQL 0005.

The granularity is the document: WiredTiger uses document-level concurrency control, so many clients can modify different documents of the same collection at once, coordinating with optimistic concurrency control and only intent locks above the document. MongoDB Manual: "WiredTiger uses document-level concurrency control for write operations. As a result, multiple clients can modify different documents of a collection at the same time. … WiredTiger uses optimistic concurrency control. WiredTiger uses only intent locks at the global, database and collection levels." Read that as the direct cousin of InnoDB's row-level locking — fine-grained, not a table lock.

Space: freed room is reused automatically — no VACUUM

This is the biggest divergence from Postgres, and the place your Postgres course pays off by contrast. In Postgres, old row versions pile up inline in the heap as dead tuples, and a background VACUUM must reclaim them or the table bloats (Postgres 0002). WiredTiger does not work that way: as documents are deleted it keeps lists of the freed space and reuses it for new data on its own — there is no VACUUM to schedule. MongoDB Manual: "The WiredTiger storage engine maintains lists of empty records in data files as it deletes documents. This space can be reused by WiredTiger…"

The key insight — but note the InnoDB-style catch

Reused automatically ≠ returned to the OS. Freed space stays inside the data file and will not be returned to the operating system except under specific steps — compact, or resyncing a replica-set member. MongoDB Manual: "…but will not be returned to the operating system unless under very specific circumstances. … resyncing a replica set member or using the compact command." That is exactly the Postgres distinction between plain VACUUM (reuse in place) and VACUUM FULL (shrink the file) — WiredTiger just does the "reuse in place" half for you continuously, and compact is its VACUUM FULL.

Durability: checkpoint + journal — the same WAL story

Nothing here should surprise you after Postgres 0009 and MySQL 0007. Two mechanisms cooperate. First, every 60 seconds WiredTiger writes a full, consistent checkpoint of a snapshot to disk — a recovery point. MongoDB Manual: "MongoDB configures WiredTiger to create checkpoints… writing the snapshot data to disk at intervals of 60 seconds. … checkpoints can act as recovery points." Second, between checkpoints, a write-ahead journal records changes so nothing committed is lost if the server dies mid-interval. MongoDB Manual: "To provide durability in the event of a failure, MongoDB uses write ahead logging to on-disk journal files. … if MongoDB exits unexpectedly in between checkpoints, journaling is required to recover information that occurred after the last checkpoint."

The journal is flushed on a tight cadence — every 100 ms, or immediately for a write that asked for it (j: true), or when a 100 MB journal file fills. MongoDB Manual: journal records are synced "At every 100 milliseconds," on j: true, and when WiredTiger creates a new (~100 MB) journal file. And recovery is the WAL algorithm you already know: find the last checkpoint id, then replay the journal records written since it. MongoDB Manual: recovery "Looks in the data files to find the identifier of the last checkpoint… Searches in the journal files for the record that matches… Apply the operations in the journal files since the last checkpoint." This is redo-style roll-forward — one log, exactly like the Postgres WAL and InnoDB's redo log.

Memory: the WiredTiger cache is the buffer pool

WiredTiger keeps the working set in an internal cache sized, by default, to the larger of 50% of (RAM − 1 GB) or 256 MB — and it also leans on the OS filesystem cache. MongoDB Manual: "the WiredTiger internal cache size is the larger of either: 50% of (RAM - 1GB), or 0.256 GB." MongoDB "utilizes both the WiredTiger internal cache and the filesystem cache." The internal cache holds data uncompressed (ready to work on) while the filesystem cache mirrors the compressed on-disk blocks. Think of the internal cache as InnoDB's buffer pool — the hot pages in RAM.

A tax neither relational engine charges by default: compression

On disk, WiredTiger compresses by default — snappy block compression for most collections and prefix compression for indexes (zstd for time-series). MongoDB Manual: "By default, WiredTiger uses block compression with the snappy compression library for most collections, and prefix compression for all indexes." So a MongoDB collection is typically smaller on disk than the same data would be in a default Postgres or InnoDB table — a real, free operational difference to remember.

Three engines, one table

QuestionInnoDBPostgresMongoDB / WiredTiger
StructureClustered B+treeHeap + secondary indexesB-tree per collection/index
MVCC old versionsUndo log (side)Inline in the heapKept by the engine (side)
Reclaim spacePurge threadsVACUUM / autovacuumAutomatic reuse — no VACUUM
Shrink the fileOPTIMIZE TABLEVACUUM FULLcompact / resync
Durability logRedo logWALJournal (+ 60 s checkpoint)
Hot pages in RAMBuffer poolshared_buffersWiredTiger cache
Write granularityRow-levelRow (MVCC)Document-level

Check yourself

Answer from memory — the effortful recall is what builds retention. Feedback is immediate.

At what granularity does WiredTiger control concurrent writes?

Document-level concurrency: many clients can modify different documents of the same collection at once, coordinated optimistically with only intent locks above the document — the cousin of InnoDB row-level locking.

When does an operation get its consistent MVCC view of the data?

WiredTiger gives each operation a point-in-time snapshot at its start — the same snapshot-isolation shape you saw in Postgres and InnoDB.

Documents are deleted all day. What reclaims that space for reuse?

WiredTiger maintains free lists and reuses the space on its own — there's no VACUUM. (But the file only shrinks back to the OS via compact or a resync.)

The server crashes 40 s after the last checkpoint. Recovery works by:

Find the last checkpoint, then roll forward by applying journal records written after it — redo-style recovery, exactly like the Postgres WAL and InnoDB redo log.

On disk, a default WiredTiger collection compared to a default relational table is:

WiredTiger applies snappy block compression to collections and prefix compression to indexes by default — a free on-disk saving neither InnoDB nor Postgres gives out of the box.
Optional — when you have an instance

No Mongo handy yet, so this is a lab for later (Docker: docker run --rm -p 27017:27017 mongo, then mongosh). It surfaces the engine facts from this lesson:

db.serverStatus().storageEngine        // { name: "wiredTiger", ... }
db.serverStatus().wiredTiger.cache["bytes currently in the cache"]
db.serverStatus().wiredTiger.cache["maximum bytes configured"]  // ≈ 50% of (RAM − 1GB)
db.stats()                             // storageSize vs dataSize — compression + free space
db.runCommand({ compact: "yourColl" }) // the "VACUUM FULL": return freed space to the OS

Watch storageSize (compressed, on disk) sit below dataSize (logical) — that's snappy at work. Bring the numbers to your teacher and we'll read them together.

Primary source — read this next

MongoDB Manual — WiredTiger Storage Engine (concurrency, snapshots, checkpoints, cache, compression — all in one page), then Journaling for the durability half. For the real internals — pages, B-tree splits, the row store — the WiredTiger Architecture Guide is the ground truth, the way Suzuki's book was for Postgres.