Skip to content
Back to Blog
By JSONConvert Team··6 min read

JSON vs JSONB in PostgreSQL: Which to Use

Use jsonb unless you have a specific reason not to. That is the PostgreSQL manual's own advice: "most applications should prefer to store JSON data as jsonb, unless there are quite specialized needs, such as legacy assumptions about ordering of object keys." Both types validate that the value is JSON. The difference is how it is stored. json keeps the exact input text and reparses it on every read. jsonb parses once on write into a binary format, which makes writes slightly slower and reads and queries much faster, and it is the only one of the two you can index with GIN.

The difference in one table

Propertyjsonjsonb
StorageExact copy of the input textDecomposed binary format
Write costLower, no conversionSlightly higher, converted on input
Read and query costReparsed on every callNo reparsing needed
WhitespacePreservedDropped
Key orderPreservedNot preserved
Duplicate keysAll kept, last one wins in functionsOnly the last value kept
GIN indexingNoYes
Containment operator @>NoYes
Everything in this table comes from the PostgreSQL documentation for the JSON types (section 8.14).

What "stores an exact copy" means in practice

Insert the same text into both types and read it back:

The json value comes back byte for byte, including the missing space after "active":. The jsonb value comes back normalised: whitespace is rewritten and the keys are reordered. If anything downstream compares the stored text to the original, or a signature was computed over the original bytes, that matters. For almost everything else it does not.

Duplicate keys work the same way. json keeps both copies of a repeated key. jsonb keeps only the last one. If you are not sure whether a payload has duplicate keys, the JSON validator will parse it, and the JSON diff tool will show you what changed between two versions.

When to use jsonb

Use jsonb when you will read inside the document from SQL. That covers most real uses:

  • filtering rows by a field inside the document
  • checking whether a key exists
  • indexing the column so those filters stay fast as the table grows
  • updating one field in place with jsonb_set
The index is the main reason. A default GIN index supports the key-exists operators (?, ?|, ?&), containment (@>) and the jsonpath match operators (@?, @@):

If you only ever query containment, the jsonb_path_ops operator class is the other option. It supports @>, @? and @@ but not the key-exists operators:

If you always filter on one nested key, an expression index on just that key is likely to be smaller and faster than indexing the whole document. The PostgreSQL docs give the example of indexing jdoc -> 'tags' on its own.

When plain json is the better choice

Pick json when the column is write-mostly and you need the original text back exactly:

  • audit logs or webhook archives where you must keep the payload as received
  • documents where key order carries meaning to some consumer
  • payloads with duplicate keys that you must not silently collapse
If you never query inside the value at all, it is also worth asking whether you need either type. A text column holds the same bytes without validation. The JSON types are better than text mainly because they reject invalid JSON at insert time.

Things jsonb rejects that json accepts

jsonb maps JSON values onto PostgreSQL types, so it inherits their limits. Per the manual:
  • \u0000 is rejected, because PostgreSQL's text type cannot hold it
  • Unicode escapes for characters the database encoding cannot represent are rejected
  • numbers outside the range of the numeric type are rejected
  • numbers are printed by numeric, so 1e3 comes back without E notation
A json column only checks the syntax, so it accepts these. If a migration from json to jsonb fails on a few rows, these are the first things to check.

Switching an existing column from json to jsonb

This rewrites the table, so on a large table plan for the lock. Any row with a value jsonb refuses will make the statement fail, and you will need to clean that row first.

JSON vs JSONB outside PostgreSQL

The name means different things in different databases:

  • MySQL has one JSON type. The MySQL manual says JSON columns are converted to an internal binary format so the server can look up values by key or array index without parsing text. There is no separate jsonb.
  • SQLite added a JSONB format in version 3.45.0, stored as a BLOB. The SQLite docs say plainly that it is not binary compatible with PostgreSQL's jsonb, despite the shared name, and that it is meant for SQLite's internal use only.
  • ORMs that support PostgreSQL generally let you pick either column type. The trade-off is the same one described here, because the ORM is only choosing the column type for you.

JSON vs BSON in MongoDB

BSON is the other "binary JSON" people mean, and it is unrelated to PostgreSQL's jsonb. BSON is MongoDB's storage and wire format, with its own specification at bsonspec.org. It encodes the type and length of each value, so a reader can skip over fields without parsing them, and it adds types JSON lacks, such as dates and raw binary data.

BSON is not always smaller than JSON. It stores every field name inside every document, plus length prefixes. MongoDB's own example: {"hello": "world"} is 22 bytes as BSON and 18 bytes as JSON text. MongoDB caps a single BSON document at 16 mebibytes.

You meet BSON directly in mongodump output, which writes .bson files. To see BSON types in a text form, MongoDB uses Extended JSON, which writes a date as an object such as {"$date": ...} so it survives the trip through plain JSON.

Generating the table from sample JSON

If you have a sample payload and want a starting schema, the JSON to SQL converter produces a CREATE TABLE and INSERT statements from it in your browser. It flattens what it can into typed columns and puts nested objects and arrays into a JSON column, which you can switch to jsonb for Postgres. The JSON to SQL guide covers when to flatten a nested field and when to keep it as a document.

FAQ

Is jsonb faster than json?

For reading and querying, yes. The PostgreSQL docs describe jsonb as "significantly faster to process, since no reparsing is needed". For inserts it is slightly slower, because the text is converted on the way in.

Does jsonb take more space than json?

It can go either way, depending on the document, and PostgreSQL's docs do not promise either. Measure your own data with pg_column_size on a sample of rows in both types before deciding on size alone.

Can I index a json column?

Not with GIN. GIN indexing is a jsonb feature. You can still build a B-tree expression index on one extracted field of a json column, for example on (payload->>'user_id').

Why did my keys change order?

You stored the value as jsonb, which does not preserve key order or whitespace. Use json if the original order must survive.

Related Tools