MongoDB Internal — Part 1

Vipul Vyas
8 min readMar 13, 2024

--

Inserting Data into MongoDB: Understanding the Process

When you execute the MongoDB query db.test.insert({hello: “world”}), a fascinating sequence of events unfolds behind the scenes, seamlessly handling the insertion of data into your MongoDB database. Let’s dissect this process step by step.

1. Parsing the Query: MongoDB’s engine first parses the query to comprehend the operation you intend to perform. In this case, it identifies it as an insertion of a document into the test collection.

2. Document Creation: Next, MongoDB crafts a new document according to your specifications. For our example, it creates a document containing a single field: hello with the value world.

3. Insertion into Collection: With the document ready, MongoDB proceeds to insert it into the test collection, ensuring that the data is stored efficiently and securely.

4. Acknowledgment: Depending on your MongoDB configuration, the server may send an acknowledgment back to your client, confirming the success of the operation. This acknowledgment varies based on factors such as write concern settings.

Now, let’s take a closer look at the output document resulting from this insertion:

{
"_id": ObjectId("4bface1a2231316e04f3c434"),
"hello": "world"
}

_id: MongoDB automatically assigns a unique identifier to each document, ensuring its distinctiveness within the collection. This _id field, represented as an ObjectId, serves as the primary key.

hello: This field represents the key specified in the document you inserted, along with its corresponding value.

ObjectId(“4bface1a2231316e04f3c434”): MongoDB generates a unique _id for the document, typically in the form of a 12-byte hexadecimal string(2 hex char = 1 byte). This identifier ensures the document’s uniqueness within the collection, facilitating efficient retrieval and management of data. The 12-byte ObjectId consists of:

  • 4-byte timestamp, representing the ObjectId’s creation, measured in seconds since the Unix epoch.
  • 5-byte random value generated once per process. This random value is unique to the machine and process.
  • 3-byte incrementing counter initialized to a random value.

Understanding MongoDB BSON

In MongoDB, BSON (Binary JSON) serves as the primary data representation format. It’s a binary-encoded serialization of JSON-like documents, optimized for efficient storage, retrieval, and manipulation within MongoDB databases.

Definition of BSON: BSON is a binary serialization format designed specifically for MongoDB. It extends JSON by introducing additional data types and binary encoding, enabling MongoDB to handle complex data structures, large volumes of data, and diverse data types more efficiently than traditional text-based formats like JSON.

How BSON Works in MongoDB:

  1. Serialization: When you insert a document into a MongoDB collection, MongoDB converts it into BSON format. This serialization process involves encoding each field and value within the document into binary form according to BSON specifications. For example, strings are represented as UTF-8 encoded binary data, numbers are stored as their binary representations, and objects are recursively encoded into BSON documents.
  2. Binary Encoding: BSON employs a binary encoding scheme, which significantly reduces the overhead associated with text-based formats like JSON. Binary encoding allows MongoDB to represent data more compactly and efficiently, resulting in reduced storage requirements and faster data access and transmission.
  3. Extended Data Types: BSON introduces additional data types beyond those supported by JSON, such as Date, Binary Data, ObjectId, and more. These data types cater to MongoDB’s diverse data storage needs and facilitate seamless integration with programming languages and frameworks. For instance, ObjectId is a unique identifier automatically generated by MongoDB for each document, aiding in document identification and retrieval.
  4. Efficient Deserialization: When retrieving data from MongoDB, BSON-encoded documents are deserialized back into their JSON-like representation. This deserialization process involves parsing the binary-encoded data and reconstructing the original document structure. BSON’s efficient deserialization contributes to faster query execution and improved overall performance in MongoDB.

Consider the following JSON document:

{
"name": "John Doe",
"age": 30,
"address": {
"street": "123 Main St",
"city": "New York"
}
}

When inserted into a MongoDB collection, MongoDB internally represents it in BSON format:

\x16\x00\x00\x00 // BSON Document Length
\x02 // Type String
name\x00 // Field Name
\x0B\x00\x00\x00 // String Length
John Doe\x00 // Field Value
\x10 // Type 32-bit integer
age\x00 // Field Name
\x1E\x00\x00\x00 // Field Value (30)
\x03 // Type Embedded Document
address\x00 // Field Name
\x1A\x00\x00\x00 // Document Length
\x02 // Type String
street\x00 // Field Name
\x0B\x00\x00\x00 // String Length
123 Main St\x00 // Field Value
\x02 // Type String
city\x00 // Field Name
\x08\x00\x00\x00 // String Length
New York\x00 // Field Value
\x00 // BSON Null Terminator

In this BSON representation:
- Each field is preceded by its type identifier.
- String values are prefixed by their length and terminated with a null byte (\x00).
- The entire document is terminated by a null byte (\x00).

This binary encoding optimizes storage and retrieval, enabling MongoDB to efficiently manage and manipulate data.

Understanding Read Operations in MongoDB

Let’s walk through the process of executing a MongoDB find query, dissecting each step and providing an example to illustrate its operation.

1. Query Parsing: MongoDB’s driver parses your query to discern the specific documents you’re targeting. This includes filters, projections, sorting criteria, and any limitations specified.
Example: Consider the query db.collection.find({name: “John”}, {age: 1}). Here, MongoDB parses the query to understand that you’re searching for documents where the “name” field equals “John” and only retrieving the “age” field.

2. Index Lookups (if applicable): If an appropriate index exists for the query filters, MongoDB leverages it to efficiently locate relevant documents on disk.
Example: If there’s an index on the name field, MongoDB uses it to quickly find documents where the name is “John”.

3. Data Page Access: MongoDB identifies the data pages containing the target documents based on the query outcome, either through index lookups or a full collection scan.
Example: MongoDB identifies the data pages that hold documents matching the query’s criteria, such as those with the nameJohn”.

4. Data Page Loading: The necessary data pages are loaded from disk into memory, potentially involving reading multiple pages if the documents span across different pages.
Example: MongoDB loads the data pages containing the documents with the nameJohn” into memory for further processing.

5. Data Retrieval from Memory (if applicable): If the requested documents are already present in memory (due to recent access or caching), MongoDB retrieves them directly from memory instead of disk.
Example: If the documents with the nameJohn” were recently accessed and are still in memory, MongoDB fetches them from memory, bypassing disk access.

6. Document Decoding: MongoDB decodes the BSON-encoded format of the stored documents back into native data types used by your application language.
Example: The BSON-encoded documents retrieved from disk (or memory) are decoded into JavaScript objects, ready for manipulation in your application.

7. Projection Application (if specified): If a projection document is specified in the query, MongoDB filters out unnecessary fields from the fetched documents, optimizing performance and reducing data transferred.
Example: In our query, {age: 1} indicates we only want the age field, so MongoDB includes only the age field in the retrieved documents.

8. Result Cursor Creation: MongoDB creates a cursor, allowing your application to access the retrieved documents one at a time. You can iterate through the cursor to process each document.
Example: After executing the query, MongoDB returns a cursor containing the documents matching the specified criteria, allowing your application to iterate through them.

Example Result:


[
{age: 30},
{age: 35}
]

In this result, MongoDB has executed the query, parsing the specified criteria, loading relevant data pages, and applying projection. The resulting cursor contains documents where the age is associated with the name “John”.

MongoDB’s Wire Protocol: Unveiling the Underpinnings of Data Transmission

The MongoDB Wire Protocol is a simple socket-based, request-response style protocol. Clients communicate with the database server through a regular TCP/IP socket.

TCP/IP Socket: Clients should connect to the database with a regular TCP/IP socket.

Port: The default port number for mongod and mongos instances is 27017. The port number for mongod and mongos is configurable and may vary.

  1. Message Structure: MongoDB’s Wire Protocol defines a standardized message structure comprising various fields and headers. Each message encapsulates a specific operation, such as query, insert, update, or delete.
  2. Message Header: The message header includes essential metadata, including the message length, request ID, response ID, opcode (operation code), and flags. These fields provide crucial information for message parsing and interpretation.
  3. Operation Code (Opcode): The opcode field specifies the type of operation encapsulated within the message. Common opcodes include insert (2002), query (2004), update (2001), delete (2006), and command (2010), each corresponding to a specific MongoDB operation.
  4. Message Body: The message body contains the payload of the message, which varies depending on the opcode. For example, in an insert message, the body comprises the BSON-encoded document to be inserted into the database collection.
  5. Data Serialization: Data transmitted via MongoDB’s Wire Protocol undergoes serialization, typically using BSON (Binary JSON) encoding. BSON encoding optimizes data representation for efficient storage and transmission, ensuring compatibility across different platforms and programming languages.

Consider an example insert message transmitted using MongoDB’s Wire Protocol:

JSON Format:

{
"messageLength": 128,
"requestID": 123456789,
"responseTo": 987654321,
"opCode": 2002,
"flags": 0,
"collectionName": "users",
"document": {
"_id": ObjectId("61a9bd7f1530482c8052968f"),
"name": "Alice",
"age": 30,
"email": "alice@example.com"
}
}

BSON Format:

\x80\x00\x00\x00 // Message Length: 128 bytes
\x15\x00\x00\x00 // Request ID: 123456789
\xf1\xe2\xd2\x07 // Response ID: 987654321
\x02\x00\x00\x00 // Opcode: 2002 (Insert)
\x00\x00\x00\x00 // Flags: 0
users\x00 // Collection Name: "users"
\x3F\x00\x00\x00 // Document Length: 63 bytes
\x10_id\x00 // Field Name: "_id"
\x1f\x1e\x53\x40\x34\x70\x5f\x16\x00\x00\x00\x00\x00\x00\x00\x00 // ObjectId("61a9bd7f1530482c8052968f")
\x02name\x00 // Field Name: "name"
\x06\x00\x00\x00Alice\x00 // String Value: "Alice"
\x10age\x00 // Field Name: "age"
\x1e\x00\x00\x00 // 30 in little-endian 32-bit integer format
\x05email\x00 // Field Name: "email"
\x11\x00\x00\x00alice@example.com\x00 // String Value: "alice@example.com"
\x00 // BSON Null Terminator
  • messageLength: Specifies the total size of the message in bytes.
  • requestID: Unique identifier for the client request.
  • responseTo: Identifier for the corresponding response message from the server.
  • opCode: Operation code indicating an insert operation (2002).
  • flags: Flags used to indicate additional options or settings associated with the operation being performed.
  • collectionName: Name of the target collection ("users").
  • document: BSON-encoded representation of the document to be inserted, including fields such as _id, name, age, and email

conclusion

In conclusion, our exploration of MongoDB’s internal mechanisms, including BSON encoding and the MongoDB Wire Protocol, has provided valuable insights into how MongoDB efficiently manages data storage and transmission. Understanding these foundational aspects enhances our ability to design optimized schemas and queries, empowering us to build robust and scalable applications leveraging MongoDB effectively. Stay tuned for further insights into MongoDB’s core components and processes in upcoming installments.

--

--