PostgreSQL Jsonb vs. MongoDB BSON: The Real Architectural Tradeoffs

amai1 pts0 comments

= 1024) leftSidebarOpen = false; if(window.innerWidth >= 1280) rightSidebarOpen = false"<br>:class="{ 'dark': $store.theme.dark, 'left-sidebar-closed': !leftSidebarDesktop, 'right-sidebar-closed': !rightSidebarDesktop, 'overflow-hidden': leftSidebarOpen || rightSidebarOpen, 'sidebars-closed': !leftSidebarDesktop && !rightSidebarDesktop }">

PostgreSQL JSONB vs MongoDB BSON: The Real Architectural Tradeoffs

Skip to content

Navigation

if (window.innerWidth

Preferences

Layout

Save Sidebar view

Remember if sidebars are open or closed across pages.

Save Sidebar layout preference

Enable Bookmarks

Save your favourite articles locally in this browser.

Enable Local Bookmarks

let scroll = window.scrollY;<br>let docHeight = document.documentElement.scrollHeight - window.innerHeight;<br>this.progress = docHeight > 0 ? Math.max(0, Math.min(1, scroll / docHeight)) : 0;<br>this.ticking = false;<br>});<br>}"<br>@scroll.window.passive="update()"<br>@resize.window.passive="update()"<br>x-init="update()"<br>class="fixed top-0 left-0 w-full h-[3px] z-[100] pointer-events-none">

PostgreSQL JSONB and MongoDB BSON may both look like JSON at the application level, but they behave differently once stored, indexed, read, and updated.

Most teams pick between Postgres and Mongo by arguing about SQL vs documents, transactions, joins, or what everyone's using at the moment. The format on disk barely makes it into the discussion. That’s the wrong place to stop, because each database’s byte level design carries the philosophy of the engine. BSON is a binary echo of MongoDB's runtime: a self describing wire format that the server can scan, mutate, and ship easily. JSONB is a parse tree frozen into a Postgres tuple: optimized for read time access, indifferent to write time mutation, and beholden to MVCC.

Once you know what each format actually is, the rest of the comparison stops being a religious argument and starts being a set of engineering tradeoffs you can reason about. This piece walks through what BSON and JSONB are at the byte level, how they behave once they hit storage, what each one costs to index, update, and read back, and which workloads each one quietly punishes.

Why the format matters more than the API

Application developers see JSON. Both engines accept JSON on the way in and serve JSON on the way out, so it is tempting to assume the storage format is a cosmetic detail. It is not. The storage format dictates:

How much byte juggling the server does on every read.

Whether a partial update can mutate a value in place or has to rewrite the entire document.

What kinds of indexes can be built and how big they get.

How much write amplification you pay when a single field changes.

Whether the disk representation survives a torn page or a partial flush.

A query that runs in 1ms on a hot row can run in 30ms if the format forces the server to materialize and reparse the row. A field update that flips a boolean can rewrite 8KB of heap, generate WAL, trigger toast churn, and invalidate three indexes, or it can patch four bytes. The format decides which world you live in.

BSON: a binary echo of MongoDB's runtime

BSON stands for Binary JSON. BSON is a length prefixed, type tagged, ordered key value format that was designed with three properties in mind: cheap to parse linearly, cheap to mutate in place when the new value is the same size, and rich enough to carry types that JSON cannot express.

The wire layout of a BSON document looks roughly like this:

int32 total_document_length_in_bytes<br>{ element }*<br>byte 0x00 // terminator

Each element is itself:

byte type_tag (0x01 double, 0x02 string, 0x03 embedded doc, 0x07 ObjectId, ...)<br>cstring field_name

Two things follow from that layout. First, every document and every embedded subdocument carries its own length prefix, which means a parser can skip an entire subtree in one pointer arithmetic step. Second, fields are ordered. The same logical document can be encoded in different byte sequences depending on insertion order, and the server preserves that order.

The type tag set is wider than JSON's. BSON has dedicated tags for:

32 bit and 64 bit integers (JSON has only a generic number).

IEEE 754 decimal128 for financial workloads.

ObjectId (12 bytes: timestamp, machine id, counter).

UTC datetime (int64 milliseconds since epoch).

Binary blobs with a subtype byte.

UUID (a binary subtype).

Regular expressions.

A JavaScript code type, mostly historical.

MinKey and MaxKey sentinels used for index bounds.

Two consequences for engineers. One: BSON round trips numeric types faithfully. A column that stores 64 bit account ids does not silently become a double the way it would in pure JSON. Two: BSON carries metadata that JSON cannot, which is part of the reason a MongoDB driver feels chatty when you push it through a strict JSON pipeline.

The clever bit, and the one that distinguishes BSON from a hundred other binary JSON formats: every field's payload is preceded by enough information that...

bson json format byte mongodb window

Related Articles