MongoDB: Storage Management
Table of Contents
1. The Big Picture
In Oracle, you think in terms of tablespaces, datafiles, segments, extents, and blocks. MongoDB is much simpler. There are no tablespaces, no datafiles to manage, and no extent allocation. Here is the high-level comparison:
ORACLE ARCHITECTURE MONGODB ARCHITECTURE
───────────────── ────────────────────
┌─────────────────────┐ ┌─────────────────────┐
│ INSTANCE │ │ mongod │
│ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │ SGA │ │ │ │ WiredTiger │ │
│ │ Buffer Cache │ │ │ │ Cache │ │
│ │ Shared Pool │ │ │ │ (50% of RAM │ │
│ │ Redo Buffer │ │ │ │ by default) │ │
│ └───────────────┘ │ │ └───────────────┘ │
│ ┌───────────────┐ │ │ ┌───────────────┐ │
│ │ PGA │ │ │ │ Per-connection│ │
│ │ Sort Area │ │ │ │ memory │ │
│ └───────────────┘ │ │ └───────────────┘ │
└─────────┬───────────┘ └─────────┬───────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ DATAFILES (.dbf) │ │ WiredTiger Files │
│ Tablespace SYSTEM │ │ collection-xxx.wt │
│ Tablespace USERS │ │ index-xxx.wt │
│ Redo Logs │ │ journal/WiredTiger │
│ Control Files │ │ WiredTiger.wt │
└─────────────────────┘ └─────────────────────┘
The key difference: In Oracle, you create tablespaces and datafiles manually. In MongoDB, the storage engine (WiredTiger) manages all files automatically. You just create a database and a collection, and MongoDB handles the rest.
Terminology mapping:
| Oracle | MongoDB | Simple Explanation |
|---|---|---|
| Database | Database | Same concept. A logical container. One mongod can host many databases. |
| Tablespace | Does not exist | MongoDB has no tablespaces. All data for a database lives in one directory. |
| Datafile (.dbf) | .wt files (WiredTiger) | MongoDB creates .wt files automatically. You never manage them manually. |
| Table | Collection | A group of documents. Created automatically on first insert. |
| Row | Document (BSON) | A single record. Stored as a BSON (binary JSON) object. Max 16 MB. |
| Column | Field | A key-value pair inside a document. No fixed schema required. |
| Index | Index | Same concept. B-tree structure. Every collection has a default _id index. |
| Block (8 KB) | Page (4 KB default) | The unit of I/O. WiredTiger reads and writes in pages. |
2. Files on Disk
In Oracle, you know exactly where your data lives: /u01/oradata/MYDB/system01.dbf, users01.dbf, etc. In MongoDB, all files live in the dbPath directory (default: /var/lib/mongodb on Linux or C:\data\db on Windows).
Here is what you will see inside the dbPath:
/var/lib/mongodb/ ← dbPath (like ORACLE_BASE/oradata)
│
├── WiredTiger ← storage engine identifier file
├── WiredTiger.wt ← main metadata file (like control file)
├── WiredTiger.lock ← lock file (prevents two mongod processes)
├── WiredTiger.turtle ← metadata checkpoint info
│
├── collection-0-xxx.wt ← collection data files
├── collection-2-xxx.wt ← (one or more per collection)
├── collection-4-xxx.wt ← (created automatically)
│
├── index-1-xxx.wt ← index files
├── index-3-xxx.wt ← (one or more per index)
│
├── journal/ ← journal directory (like redo logs)
│ ├── WiredTigerLog.0000000001
│ ├── WiredTigerLog.0000000002
│ └── WiredTigerLog.0000000003
│
├── diagnostic.data/ ← monitoring data (like AWR snapshots)
│
├── myFirstDB/ ← (if using directoryPerDB option)
│ ├── collection-xxx.wt
│ └── index-xxx.wt
│
└── _mdb_catalog.wt ← catalog of all databases and collections
← (like the data dictionary)
Key points for Oracle DBAs:
- No manual file management: You never create, resize, or add datafiles. WiredTiger creates and grows .wt files automatically as needed.
- No pre-allocation: Oracle pre-allocates datafiles (e.g., 1 GB). MongoDB files grow dynamically. This saves disk space but means file growth happens during writes.
- One directory per database (optional): By default, all databases share the same directory. If you set
storage.directoryPerDB: truein the config file, each database gets its own subdirectory. This is similar to Oracle’s OFA structure but optional. - File naming: The file names like
collection-0-xxx.wtare internal identifiers. You cannot tell which collection a file belongs to by looking at the name. Usedb.stats()orcollStatsto see storage details. - Do NOT touch these files: Never copy, move, rename, or delete .wt files while mongod is running. This will corrupt the database. Always use
mongodumpandmongorestorefor backups (like RMAN in Oracle).
Oracle vs MongoDB file comparison:
| Oracle | MongoDB |
|---|---|
system01.dbf, users01.dbf | collection-xxx.wt |
redo01.log, redo02.log | journal/WiredTigerLog.xxx |
control01.ctl | WiredTiger.wt + _mdb_catalog.wt |
temp01.dbf | No separate temp files (sorts spill to disk in the same dbPath) |
undotbs01.dbf | No separate undo (MVCC is handled inside the B-tree) |
3. WiredTiger Cache (Your SGA)
In Oracle, the SGA (System Global Area) is the shared memory area that includes the buffer cache, shared pool, and redo log buffer. You size it with SGA_TARGET or MEMORY_TARGET.
In MongoDB, the equivalent is the WiredTiger cache. It is the in-memory area where WiredTiger keeps frequently accessed data pages and index pages.
ORACLE SGA WIREDTIGER CACHE
────────── ────────────────
┌──────────────────────┐ ┌──────────────────────┐
│ Buffer Cache │ │ Data pages │
│ (table and index │ │ (collection data) │
│ blocks from disk) │ │ │
├──────────────────────┤ ├──────────────────────┤
│ Shared Pool │ │ Index pages │
│ (parsed SQL, plans) │ │ (all index B-trees) │
├──────────────────────┤ ├──────────────────────┤
│ Redo Log Buffer │ │ (journal is written │
│ (redo entries before │ │ directly to disk, │
│ flush to redo log) │ │ not cached here) │
└──────────────────────┘ └──────────────────────┘
Default size: Default size:
SGA_TARGET = you set it 50% of (RAM - 1 GB)
minimum 256 MB
How to check and set the cache size:
// Check current cache usage
db.serverStatus().wiredTiger.cache
// Key fields to look at:
// "bytes currently in the cache" ← how much data is in memory
// "maximum bytes configured" ← the cache size limit
// "tracked dirty bytes in the cache" ← modified pages not yet on disk
// "pages read into cache" ← disk reads (like physical reads)
// "pages written from cache" ← disk writes (like DBWR writes)
To set the cache size, edit the MongoDB config file (/etc/mongod.conf):
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 4 # Set to 4 GB (like setting SGA_TARGET = 4G)
Rule of thumb (like Oracle’s 80% rule):
- If MongoDB is the only application on the server, leave the default (50% of RAM). WiredTiger also uses the OS filesystem cache, so the effective cache is larger than the WiredTiger cache alone.
- If MongoDB shares the server with other applications, set
cacheSizeGBexplicitly to avoid memory pressure. - Do NOT set the cache to more than 60–70% of total RAM. The OS needs memory for the filesystem cache and other processes.
Important difference from Oracle: In Oracle, if the buffer cache is too small, you get lots of physical reads and performance drops. In MongoDB, the same thing happens — if the WiredTiger cache is too small, WiredTiger must read pages from disk more often. You can monitor this with db.serverStatus().wiredTiger.cache["pages read into cache"]. A high and growing number means your cache may be too small for your workload.
4. Journal and Checkpoints (Your Redo Logs)
This is the most important concept for durability. If you understand Oracle redo logs and checkpoints, you already understand 90% of MongoDB’s journal.
ORACLE MONGODB
────── ───────
1. User does INSERT/UPDATE/DELETE 1. User does insert/update/delete
2. Change goes to buffer cache 2. Change goes to WiredTiger cache
(in memory) (in memory)
3. Redo entry written to 3. Journal entry written to
redo log buffer journal buffer
4. LGWR flushes redo buffer 4. WiredTiger flushes journal
to redo log files on disk to journal files on disk
(every 3 seconds, or at (every 100 ms by default,
commit, or when 1/3 full) or immediately if j:true)
5. DBWR writes dirty blocks 5. Checkpoint writes dirty pages
from buffer cache to from WiredTiger cache to
datafiles on disk .wt files on disk
(incremental checkpoint) (every 60 seconds by default)
6. After checkpoint, old redo 6. After checkpoint, old journal
logs can be overwritten files can be removed
Journal details:
| Oracle Redo Logs | MongoDB Journal |
|---|---|
Location: redo01.log, redo02.log | Location: journal/WiredTigerLog.xxx |
| Size: you set it (e.g., 500 MB each) | Size: max 100 MB per file, auto-rotated |
| Groups: minimum 2 groups | Files: multiple files, old ones removed after checkpoint |
| Content: physical block changes | Content: logical operations (compressed) |
| Flush trigger: commit, 3 sec, 1/3 full | Flush trigger: 100 ms interval, or j:true |
| ARCHIVELOG mode for backup | Journal always on (cannot be disabled in replica sets) |
Checkpoint details:
In Oracle, a checkpoint writes all dirty blocks from the buffer cache to the datafiles. In MongoDB, a checkpoint writes all dirty pages from the WiredTiger cache to the .wt files.
- Frequency: MongoDB checkpoints every 60 seconds by default (configurable). Oracle uses incremental checkpoints that happen continuously.
- What happens: During a checkpoint, WiredTiger writes all modified (dirty) pages to disk. After the checkpoint completes, the journal entries before the checkpoint are no longer needed and can be removed.
- Crash recovery: If mongod crashes, on restart WiredTiger replays the journal from the last checkpoint. This is exactly like Oracle’s instance recovery (roll forward from the last checkpoint using redo logs).
Write concern and the journal (like COMMIT in Oracle):
In Oracle, a COMMIT forces LGWR to flush the redo buffer to disk. In MongoDB, the write concern controls when the write is acknowledged:
| Write Concern | What It Means | Oracle Equivalent |
|---|---|---|
w: 1 (default) | Primary has the data in memory. Journal may not be flushed yet (up to 100 ms delay). | Like a commit where LGWR has not yet flushed (not possible in Oracle — commit always flushes redo). |
w: 1, j: true | Primary has flushed the journal to disk. Safest single-node option. | Exactly like Oracle COMMIT (LGWR flushes redo to disk). |
w: "majority" | Majority of replica set members have the data. | Like Data Guard SYNC mode (commit waits for standby acknowledgment). |
w: 0 | Fire and forget. No acknowledgment. | No Oracle equivalent (Oracle always acknowledges). |
5. Space Management and Compression
In Oracle, you manage space with tablespaces, autoextend, quotas, and segment space management (ASSM). In MongoDB, space management is almost entirely automatic. Here is what you need to know:
5a. How Space Grows
WiredTiger files grow automatically as you insert data. You do not pre-allocate space. Think of it like an Oracle datafile with AUTOEXTEND ON and no MAXSIZE — it just keeps growing until the disk is full.
ORACLE MONGODB
────── ───────
CREATE TABLESPACE users (nothing — just insert data)
DATAFILE '/u01/users01.dbf'
SIZE 1G AUTOEXTEND ON
MAXSIZE 10G;
You monitor: You monitor:
DBA_DATA_FILES db.stats().storageSize
DBA_FREE_SPACE db.stats().freeStorageSize
DBA_SEGMENTS db.collection.stats().storageSize
5b. Compression (Your Secret Weapon)
This is where MongoDB shines compared to Oracle. WiredTiger compresses data by default, so your data on disk is much smaller than the actual data in memory.
| Level | Default Algorithm | Oracle Equivalent |
|---|---|---|
| Collection data | Snappy (fast, moderate compression) | Advanced Row Compression (requires license) |
| Indexes | Prefix compression (reduces duplicate key prefixes) | Index Key Compression (ALTER INDEX … COMPRESS) |
| Journal | Snappy | Redo log compression (12c+, not common) |
Available compression options:
- none — no compression (fastest CPU, most disk space)
- snappy — default for collections and journal (good balance)
- zlib — better compression than Snappy but uses more CPU
- zstd — best compression, available from MongoDB 4.2 (good for cold data)
Real-world example: If you insert 10 GB of JSON data into MongoDB with Snappy compression, the .wt files on disk may be only 3–5 GB. In Oracle, the same data in an uncompressed table would take the full 10 GB (plus index space).
5c. Space After Deletes (The Fragmentation Problem)
This is very similar to Oracle. When you delete rows in Oracle, the space in the data blocks is freed but the datafile does not shrink. The same thing happens in MongoDB.
BEFORE DELETE AFTER DELETE (1 million docs removed)
───────────── ──────────────────────────────────────
storageSize: 10 GB storageSize: 10 GB ← same!
dataSize: 8 GB dataSize: 3 GB ← smaller
free space: 2 GB free space: 7 GB ← more holes
The .wt files do NOT shrink. The space is reused for future
This is like Oracle HWM. inserts, like Oracle free blocks.
How to reclaim space:
| Method | MongoDB Command | Oracle Equivalent |
|---|---|---|
| Compact one collection | db.runCommand({ compact: "students" }) | ALTER TABLE students SHRINK SPACE |
| Drop and recreate | db.students.drop() then re-insert | TRUNCATE TABLE or DROP + CREATE |
| Initial sync (replica set) | Resync the secondary from the primary | Rebuild the standby database |
Warning: The compact command locks the collection during the operation. Run it during a maintenance window, just like you would schedule ALTER TABLE ... MOVE in Oracle.
6. Useful Storage Commands
Here are the commands you will use most often as a DBA. Think of these as your MongoDB versions of DBA_DATA_FILES, DBA_SEGMENTS, and V$SGA.
6a. Database-Level Stats (like DBA_DATA_FILES)
// Overall database size
db.stats()
// Key fields in the output:
// "db" → database name
// "collections" → number of collections (like number of tables)
// "objects" → total number of documents (like total rows)
// "dataSize" → uncompressed data size in bytes
// "storageSize" → actual disk space used (after compression)
// "indexSize" → total size of all indexes
// "fileSize" → total size of all .wt files (deprecated in newer versions)
Example output:
{
"db": "myFirstDB",
"collections": 5,
"objects": 1500000,
"dataSize": 2147483648, // 2 GB uncompressed
"storageSize": 858993459, // 800 MB on disk (compression!)
"indexSize": 268435456, // 256 MB for indexes
"ok": 1
}6b. Collection-Level Stats (like DBA_SEGMENTS)
// Stats for a specific collection
db.students.stats()
// Or using the aggregation framework (MongoDB 5.0+)
db.students.aggregate([ { $collStats: { storageStats: {} } } ])
// Key fields:
// "count" → number of documents (like NUM_ROWS)
// "size" → uncompressed data size
// "storageSize" → disk space used
// "avgObjSize" → average document size in bytes
// "nindexes" → number of indexes
// "totalIndexSize" → total index size
6c. Index Stats (like DBA_INDEXES)
// List all indexes on a collection
db.students.getIndexes()
// Size of each index
db.students.stats().indexSizes
// Example output:
// { "_id_": 12345678, "age_1": 8765432, "name_1": 9876543 }
// Detailed index usage stats (which indexes are actually used?)
db.students.aggregate([ { $indexStats: {} } ])
// This is like V$OBJECT_USAGE in Oracle — it tells you
// which indexes are being used and which are not.
6d. Cache and Memory Stats (like V$SGA / V$BUFFER_POOL)
// WiredTiger cache details
db.serverStatus().wiredTiger.cache
// Key fields:
// "maximum bytes configured" → cache size limit (like SGA_TARGET)
// "bytes currently in the cache" → current usage
// "tracked dirty bytes in the cache" → modified pages (like dirty buffers)
// "pages read into cache" → disk reads (like physical reads)
// "pages written from cache" → disk writes (like DBWR writes)
// Overall memory usage
db.serverStatus().mem
// "resident" → actual RAM used by mongod (in MB)
// "virtual" → virtual memory (in MB)
6e. Disk Space (like df -h or ASM disk groups)
// MongoDB does not have a built-in "disk free" command.
// Use the OS command:
// Linux: df -h /var/lib/mongodb
// Windows: dir C:\data\db
// But you can check the total storage used by all databases:
db.adminCommand({ listDatabases: 1 })
// This lists every database and its size on disk.
6f. Quick Reference Card
| What You Want to Know | MongoDB Command | Oracle Equivalent |
|---|---|---|
| Total database size | db.stats() | SELECT SUM(bytes) FROM dba_segments |
| Collection size | db.students.stats() | DBA_SEGMENTS WHERE segment_name = 'STUDENTS' |
| Document count | db.students.countDocuments({}) | SELECT COUNT(*) FROM students |
| Index list | db.students.getIndexes() | DBA_INDEXES WHERE table_name = 'STUDENTS' |
| Index usage | db.students.aggregate([{$indexStats:{}}]) | V$OBJECT_USAGE |
| Cache usage | db.serverStatus().wiredTiger.cache | V$SGA / V$BUFFER_POOL_STATISTICS |
| All databases and sizes | db.adminCommand({listDatabases:1}) | DBA_DATA_FILES GROUP BY tablespace_name |
| Current operations | db.currentOp() | V$SESSION / V$SQL |
| Server status | db.serverStatus() | V$SYSSTAT / V$INSTANCE |
| Reclaim space | db.runCommand({compact:"students"}) | ALTER TABLE students SHRINK SPACE |
7. Oracle Comparison Table
A complete side-by-side reference for Oracle DBAs:
| Concept | Oracle | MongoDB |
|---|---|---|
| Storage engine | Oracle Database (proprietary) | WiredTiger (default since 3.2) |
| Database | One database per instance (or CDB/PDB in 12c+) | Many databases per mongod process |
| Tablespace | Logical storage container (SYSTEM, USERS, TEMP, UNDO) | Does not exist |
| Datafile | .dbf files, manually created and sized | .wt files, automatically created and grown |
| Block / Page size | 8 KB default (configurable: 2K–32K) | 4 KB internal page (not configurable) |
| Row / Document limit | ~8 KB per row (non-LOB), LOBs stored out-of-line | 16 MB per document |
| Schema | Fixed (CREATE TABLE with columns and types) | Flexible (schema validation is optional) |
| Memory (SGA) | SGA_TARGET / MEMORY_TARGET | wiredTiger.cacheSizeGB (default: 50% of RAM – 1 GB) |
| Buffer cache | Part of SGA, stores data blocks | WiredTiger cache, stores data and index pages |
| Shared pool | Parsed SQL, execution plans, dictionary cache | No equivalent (no SQL parsing needed) |
| Redo logs | Online redo logs (groups, members, archivelog) | Journal (journal/ directory, auto-rotated) |
| Redo flush | LGWR: at commit, every 3 sec, 1/3 full | Every 100 ms, or immediately with j:true |
| Checkpoint | Incremental (continuous), full (log switch) | Every 60 seconds (configurable) |
| Undo | Undo tablespace, undo segments, undo_retention | No separate undo (MVCC in the B-tree itself) |
| Temp space | TEMP tablespace for sorts and hash joins | No separate temp (sorts spill to dbPath if > 100 MB) |
| Compression | Optional (HCC, ACO — requires license) | On by default (Snappy for data, prefix for indexes) |
| Space reclamation | SHRINK SPACE, MOVE, TRUNCATE | compact, drop, initial sync |
| Backup | RMAN (hot/cold backup, incremental) | mongodump/mongorestore, filesystem snapshot, MongoDB Cloud Manager |
| Data dictionary | SYS schema, DBA_* views | _mdb_catalog.wt, db.serverStatus(), db.stats() |
| Monitoring | AWR, ASH, V$ views, OEM | db.serverStatus(), profiler, MongoDB Atlas, mongostat, mongotop |