MongoDB bezeichnet sich selbst als “Document Database” und Dokumente werden als JSON dargestellt – zumindest aus Entwickler-Sicht. Ein mongosh-Query returned Dokumente, die wie JSON aussehen. Die API akzeptiert JSON-ähnliche Strukturen. Aber intern speichert MongoDB kein JSON. Es nutzt BSON – Binary JSON, eine binäre Serialisierung, die fundamental anders ist als textbasiertes JSON.
Diese Dualität – JSON als Interface, BSON als Storage – ist zentral für MongoDB’s Design. Sie erlaubt die Developer Experience von JSON (leicht lesbar, universell verstanden, flexibel) mit der Performance und Typen-Reichhaltigkeit von BSON (kompakt, schnell zu parsen, mehr Datentypen). Das Verständnis beider Formate und ihrer Trade-offs ist essentiell, um MongoDB effektiv zu nutzen und Performance-Probleme zu vermeiden.
JavaScript Object Notation – JSON – wurde Anfang der 2000er Jahre populär als lightweight Alternative zu XML. Die Syntax ist simpel und intuitiv, abgeleitet von JavaScript’s Objekt-Literalen. Ein JSON-Dokument ist entweder ein Objekt (Key-Value-Paare in geschweiften Klammern) oder ein Array (geordnete Liste in eckigen Klammern).
Ein typisches JSON-Objekt für einen User könnte so aussehen:
{
"userId": 12345,
"username": "alice_smith",
"email": "alice@example.com",
"isActive": true,
"registeredAt": "2024-01-15T10:30:00Z",
"roles": ["user", "moderator"],
"profile": {
"firstName": "Alice",
"lastName": "Smith",
"age": 28
},
"metadata": null
}Die Struktur ist selbsterklärend. Keys sind Strings in doppelten
Anführungszeichen, Values können verschiedene Typen sein. Das
profile-Feld ist ein nested Object – JSON unterstützt
beliebige Verschachtelung. Das roles-Array enthält Strings.
Das metadata-Feld ist explizit null, was “kein
Wert” signalisiert.
JSON’s Datentypen sind bewusst minimal gehalten. Es gibt nur sechs:
String, Number, Boolean, Array, Object und null. Strings sind immer in
doppelten Anführungszeichen. Numbers können Integer oder Floating-Point
sein, aber JSON unterscheidet nicht – “42” und “42.0” sind beide
Numbers. Booleans sind die Literals true und
false (lowercase, ohne Anführungszeichen). Arrays und
Objects können beliebige Werte enthalten, inklusive verschachtelte
Arrays und Objects.
Diese Simplizität ist JSON’s Stärke und Schwäche. Stärke: Jede Programmiersprache kann JSON trivial parsen und generieren. Parser sind winzig und schnell. Schwäche: Wichtige Datentypen fehlen. Dates gibt es nicht – man muss Strings verwenden und hoffen, dass alle dieselbe Format-Konvention nutzen. Binary Data gibt es nicht – man muss Base64-Encoding verwenden, was 33% Overhead bedeutet. Präzise Dezimalzahlen für Finanzberechnungen gibt es nicht – man nutzt Strings oder akzeptiert Floating-Point-Ungenauigkeit.
JSON ist textbasiert, was human-readable bedeutet. Man kann ein
JSON-Dokument in einem Text-Editor öffnen und verstehen, was drin steht.
Aber textbasiert heißt auch: ineffizient für Maschinen. Jedes Zeichen
ist ein Byte (oder mehrere bei Unicode). Die Number
123456789 braucht 9 Bytes als Text, könnte aber als 32-bit
Integer in 4 Bytes gespeichert werden. Keys sind wiederholt in jedem
Dokument – in einer Collection mit Millionen User-Dokumenten ist
“username” Millionen mal gespeichert.
BSON wurde von MongoDB’s ursprünglichen Entwicklern als Antwort auf JSON’s Limitationen designed. Es ist keine neue Sprache, sondern eine binäre Serialisierung von JSON-ähnlichen Dokumenten. BSON-Dokumente haben dieselbe logische Struktur wie JSON – Objects, Arrays, nested Structures – aber die physische Repräsentation ist fundamental anders.
Ein BSON-Dokument beginnt mit einem 4-Byte-Integer, der die Gesamtlänge des Dokuments angibt. Dies erlaubt schnelles Überspringen von Dokumenten – wenn man nur das dritte Dokument in einer Sequenz will, kann man die ersten zwei überspringen, indem man deren Länge liest und den Filepointer entsprechend vorwärts bewegt. JSON hat keine solche Metadaten – man muss parsen, um zu wissen, wo ein Dokument endet.
Jedes Feld in BSON besteht aus drei Teilen: einem Type-Byte, einem null-terminierten Key-String und einem Value. Das Type-Byte kodiert den Datentyp – String, Integer, Double, Boolean, Array, Object, und viele weitere. Dies erlaubt effiziente Speicherung: Ein Integer wird als 4 oder 8 Bytes gespeichert, nicht als Text. Ein Boolean ist ein einzelnes Byte (0 oder 1), nicht die Strings “true” oder “false”.
BSON’s zusätzliche Datentypen sind der Hauptvorteil über JSON. Der
wichtigste ist ObjectId – ein 12-Byte-Identifier, der in
MongoDB als Default für _id-Felder verwendet wird. Ein
ObjectId ist nicht nur einzigartig, sondern embedded auch einen
Timestamp (die ersten 4 Bytes sind Sekunden seit Unix-Epoch). Dies
erlaubt sortieren nach Creation-Time ohne separates
createdAt-Feld.
// ObjectId-Struktur (12 Bytes total)
// [4 Bytes Timestamp][5 Bytes Random][3 Bytes Counter]
ObjectId("507f1f77bcf86cd799439011")Der Date-Typ ist ein 64-bit Integer, der Millisekunden
seit Unix-Epoch repräsentiert. Dies ist präzise, sortierbar und
international – keine Timezone-Konfusion, keine String-Parsing-Fehler.
MongoDB’s ISODate("2024-01-15T10:30:00Z") ist intern ein
BSON Date.
Der Decimal128-Typ ist eine 128-bit Dezimalzahl mit
exakter Präzision. Für Finanzdaten, wo
0.1 + 0.2 = 0.30000000000000004
(Floating-Point-Ungenauigkeit) inakzeptabel ist, ist Decimal128
essentiell. Es speichert Werte wie 19.99 exakt, ohne
Rundungsfehler.
BinData erlaubt effizienten Storage von binären Daten –
Images, PDFs, verschlüsselte Blobs. Statt Base64-Encoding (33% Overhead)
speichert man die rohen Bytes direkt. MongoDB unterstützt verschiedene
BinData-Subtypes – Generic, Function, UUID, MD5, Encrypted, etc.
RegEx speichert reguläre Ausdrücke nativ. Queries können
direkt gegen RegEx-Patterns matchen, ohne String-Serialisierung:
db.users.insertOne({
username: "alice",
emailPattern: /^[a-z]+@example\.com$/i
})Die vollständige BSON-Type-Liste umfasst über 20 Typen, von
MinKey und MaxKey (spezielle Boundary-Werte
für Sortierung) über Timestamp (intern für Replication) bis
JavaScript und JavaScript with Scope
(Code-Speicherung, selten genutzt). Diese Typen-Reichhaltigkeit erlaubt
präzise Datenmodellierung ohne Workarounds.
Die binäre Natur von BSON bringt messbare Performance-Vorteile. Das Parsen ist schneller, weil keine String-zu-Number-Konversionen nötig sind. Ein BSON-Integer ist bereits als 32-bit oder 64-bit Integer kodiert – direktes Memory-Mapping ist möglich. JSON-Numbers müssen geparst werden, Character für Character, mit Fehlerbehandlung für invalide Syntax.
Die Traversierung ist schneller wegen der Length-Prefixes. BSON-Arrays und Objects beginnen mit ihrer Gesamtlänge. Um ein nested Object zu überspringen, liest man die Länge und springt vorwärts. JSON erfordert vollständiges Parsen aller nested Structures, um zu wissen, wo sie enden.
Ein konkretes Benchmark-Szenario: Eine Collection mit 1 Million User-Dokumenten. Jedes Dokument hat 20 Felder, inklusive nested Profile-Object und Arrays. Als JSON würde dies etwa 200 MB benötigen (angenommen durchschnittlich 200 Bytes pro Dokument). Als BSON sind es etwa 150 MB – 25% kompakter. Das Laden und Parsen ist 2-3x schneller bei BSON.
Der Overhead von BSON ist die Metadaten. Jedes Feld hat ein Type-Byte
und null-Terminierung des Keys. Für kleine Dokumente mit wenigen Feldern
kann BSON tatsächlich größer sein als JSON. Ein Dokument
{"a":1} ist 7 Bytes in JSON, aber etwa 12 Bytes in BSON
(Document-Length + Type + Key + Null-Terminator + Value +
Document-End-Marker). Bei größeren, komplexen Dokumenten amortisiert
sich der Overhead und BSON ist kompakter.
MongoDB nutzt BSON intern für Storage und Netzwerk-Protocol. Wenn eine App ein Dokument inserted, sendet der Treiber es als BSON über das Netzwerk. mongod empfängt BSON, speichert es fast direkt (mit minimalen Transformationen). Reads funktionieren umgekehrt: mongod liest BSON von Disk, sendet es über Netzwerk, der Treiber deserialisiert zu nativen Language-Objekten (etwa Python Dicts oder JavaScript Objects).
Dieser gesamte Pfad vermeidet JSON-Parsing. Die einzige Stelle, wo JSON involviert ist: Developer-Tools wie mongosh. mongosh zeigt Dokumente als JSON-ähnlich für Lesbarkeit, aber intern kommuniziert es in BSON. Dies ist reine Convenience für Menschen.
MongoDB’s Developer Experience basiert auf JSON, weil JSON universell ist. APIs in allen Sprachen akzeptieren JSON-ähnliche Strukturen. JavaScript-Entwickler nutzen Objekt-Literale direkt. Python-Entwickler nutzen Dicts. Die Cognitive Load ist minimal – jeder, der mit modernen Programmiersprachen arbeitet, versteht JSON intuitiv.
# Python mit PyMongo
user = {
"username": "alice",
"email": "alice@example.com",
"age": 28,
"registered": datetime.now()
}
collection.insert_one(user)PyMongo konvertiert dieses Python-Dict automatisch zu BSON für die
Netzwerk-Übertragung. Das datetime-Objekt wird zu BSON
Date. Der Developer sieht nie BSON-Bytes. Die Abstraktion ist
perfekt.
Aber manchmal ist Awareness von BSON nützlich. Performance-Debugging
etwa: Warum ist eine Query langsam? Möglicherweise weil die Dokumente
riesig sind und viel Netzwerk-Transfer benötigen.
db.collection.stats() zeigt avgObjSize in
Bytes – die durchschnittliche BSON-Größe pro Dokument. Wenn dies mehrere
Megabytes ist, ist das ein Problem.
Oder Type-Confusion: Ein Feld, das als String gespeichert wurde,
obwohl es numerisch sein sollte. Queries mit $gt oder
$lt funktionieren nicht korrekt, weil String-Vergleich
statt numerischer Vergleich passiert:
// Falsch: age als String gespeichert
db.users.insertOne({ age: "28" })
db.users.find({ age: { $gt: 25 } }) // Findet nichts!
// Korrekt: age als Number
db.users.insertOne({ age: 28 })
db.users.find({ age: { $gt: 25 } }) // Findet das DokumentMongoDB speichert Typen explizit in BSON. “28” (String) und 28 (Number) sind unterschiedliche Typen und matchen nicht in Queries. Dies ist ein häufiger Fehler bei Migrationen aus schemaless-Systemen oder wenn Daten aus externen Quellen importiert werden.
Die $type-Operator in Queries kann Typ-Inkonsistenzen
finden:
// Finde alle Dokumente, wo age ein String ist (sollte Number sein)
db.users.find({ age: { $type: "string" } })Type-Codes sind dokumentiert: 1 für Double, 2 für String, 16 für
Int32, 18 für Int64, etc. In neueren MongoDB-Versionen kann man auch
Type-Namen verwenden: $type: "double" statt
$type: 1.
BSON-Dokumente haben ein hartes Limit: 16 MB maximal. Dies ist keine willkürliche Einschränkung, sondern ein Design-Entscheidung. Dokumente sollten “reasonable size” sein. Ein 16-MB-Dokument kann etwa 200.000 Felder haben oder mehrere tausend nested Objects – mehr als jede vernünftige Anwendung braucht.
Das Limit existiert aus mehreren Gründen. Technisch: MongoDB lädt komplette Dokumente in RAM für Operationen. 16 MB ist groß genug für komplexe Daten, aber klein genug, dass viele Dokumente gleichzeitig im RAM sein können. Theoretisch unbegrenzte Dokumente würden RAM-Management komplizieren.
Praktisch: Riesige Dokumente sind ein Design-Smell. Sie deuten auf falsche Daten-Modellierung hin. Ein User-Dokument mit einem Array von 100.000 Orders ist schlecht designed. Orders sollten eine separate Collection sein, referenziert durch ID. Embedding ist für kleine, häufig zusammen abgefragte Daten gedacht, nicht für unbegrenzte Listen.
Wenn man wirklich größere Daten speichern muss – etwa PDFs oder
Videos – nutzt man GridFS. GridFS ist ein Spec, das große Files in
255-KB-Chunks aufteilt und in zwei Collections speichert:
fs.files (Metadaten) und fs.chunks (Daten).
MongoDB-Treiber abstrahieren dies und erlauben
Streaming-Uploads/-Downloads.
Das 16-MB-Limit gilt nur für einzelne Dokumente. Collections sind praktisch unbegrenzt. Eine Collection mit Milliarden Dokumenten ist kein Problem, solange jedes Dokument unter 16 MB ist.
Für API-Responses an Web-Clients ist JSON die Wahl. Browsers parsen
JSON nativ mit JSON.parse(). Die Lesbarkeit erlaubt
einfaches Debugging in DevTools. Die Universalität garantiert
Kompatibilität mit jedem Frontend-Framework.
Für MongoDB-Storage und interne Kommunikation ist BSON automatisch – man wählt nicht, es passiert einfach. Die Treiber handhaben die Konversion transparent.
Für Daten-Exports gibt es Trade-offs. mongodump erzeugt
BSON-Files, die kompakt und schnell zu restoren sind. Aber sie sind
binär und nicht human-readable. Für Audits oder manuelle Inspektion ist
mongoexport mit JSON-Output besser. Es ist größer und
langsamer, aber man kann es in Text-Editoren öffnen oder mit
jq prozessieren.
Für Backup-Strategien ist BSON überlegen. Ein mongodump
eines 100-GB-Datasets erzeugt etwa 100 GB BSON-Files. Ein
mongoexport zu JSON erzeugt etwa 130-150 GB. Die
Restore-Zeit ist ebenfalls dramatisch unterschiedlich – BSON ist 3-5x
schneller.
Die folgende Tabelle fasst die Charakteristiken zusammen:
| Aspekt | JSON | BSON |
|---|---|---|
| Format | Textbasiert | Binär |
| Lesbarkeit | Human-readable | Maschinenlesbar |
| Größe | Größer (Text-Overhead) | Kompakter (binär) |
| Parse-Speed | Langsamer | Schneller |
| Datentypen | 6 Basis-Typen | 20+ Typen |
| Date/Time | String-Workaround | Native Date |
| Binary Data | Base64 (+33% Overhead) | Native BinData |
| Präzise Decimals | Keine | Decimal128 |
| Max Document Size | Unbegrenzt (theoretisch) | 16 MB |
| Use-Case | API, Config, Data Exchange | Database Storage, Performance |
JSON und BSON sind komplementär, nicht konkurrierend. JSON ist das universelle Interface – menschenfreundlich, sprachunabhängig, einfach. BSON ist die maschinenoptimierte Implementierung – schnell, typenreich, effizient. MongoDB’s Genius war, beide zu kombinieren: Die Developer Experience von JSON mit der Performance von BSON. Entwickler denken in JSON, MongoDB arbeitet in BSON. Diese Abstraktion ist so nahtlos, dass viele MongoDB-Nutzer BSON’s Existenz kaum bewusst wahrnehmen – bis Performance-Probleme oder Type-Confusion sie zwingt, tiefer zu graben. Dann ist das Verständnis beider Formate essentiell.