In a recent article, we began an examination of MongoDB storage engines by explaining
why WiredTiger is the current default engine. In this piece, we'll continue discussing storage engines and start to look at the specific characteristics of other available options, beginning with MMAPv1, to help readers understand which one is right for their system and workloads.
MMAPv1 is the original MongoDB storage engine. It was first included as the database's default engine, until it was replaced by WiredTiger in MongoDB 3.2. It stores and accesses data via memory-mapped files, which means that it directly loads and modifies a file’s contents in virtual memory via the
syscall—hence the engine's name. All records in MMAPv1 are contiguously located on disk, which is excellent for sequential access.
In MMAPv1, data files are given pre-allocated space, so that inserting/updating documents does not necessarily trigger I/O. This design essentially chooses to pay a penalty on storage in exchange for a boost in performance. The allocation strategy is configurable, and by default it uses “power of 2.” Alternatively, it can use the “
smallFiles” strategy if your databases are small.
"Power of 2 allocation" is a concept that allows for smoother document growth. It refers to a system where all documents are stored in records that are sized in multiples of 2 MB (i.e. 32, 64, 128, 256, etc.). When a document is first stored, the size is rounded up to the nearest multiple of 2 MB and allocated to a record of that amount. For example, a document of 190 MB would be rounded up and stored in a record of 256 MB, providing 66 MB of free space. This built-in freenspace acts as padding, which means that the documents inside have room to grow, reducing the required number of reallocations the system will need to make when documents reach their limits of available space.
When using MMAPv1, MongoDB automatically uses all free memory on the machine as its cache. As mentioned in our prior article about correctly sizing working sets, optimal performance is achieved when the working set fits into memory. Additionally, MMAPv1 flushes changes to data to disk every 60 seconds by default, though it can also be set to flush more frequently—the user is able to configure the maximum amount of time between flushes.
Unfortunately, a system crash has the potential to cause lost data when using MMAPv1, unless the journal is enabled. In that case, the journal log is written every 100ms. While still presenting a risk for data loss, the window is much smaller.
Finally, MMAPv1 uses collection-level locking, which means that two clients cannot access the same collection for writes at the same time; a write will block all reads to a collection. As a result, MMAPv1 is not particularly scalable, due to the coarse concurrency. Segmenting data into additional collections is a potential fix, allotting MMAPv1 additional concurrency.
With these details of MMAPv1's architecture in mind, what are some of the advantages it offers users?
- Data is flushed to disk infrequently, so write performance is good, with a low number of concurrent writers.
- Individual fields can be updated/written to the database, so large documents can be updated quickly—again, with a low number of concurrent writers.
- Reads are very efficient, providing that you've configured your working-set to fit into your memory.
- There is reduced on-disk fragmentation, due to MMAPv1's pre-allocation strategy.
And these are a few of the drawbacks that come along with using MMAPv1:
- There is locking on the collection level. This means that MMAPv1 does not scale well with concurrent writers!
- There is a built-in penalty on storage. Because space is pre-allocated and padded (see the explanation of "power of 2 allocation" above) , documents occupy more space on disk than the data would normally need.
- Documents that grow significantly after initial storage can trigger additional I/O and cause performance to suffer.
- Performance will drop considerably if your working set size exceeds your memory.
Overall, MMAPv1's features make it a poor choice as a general purpose storage engine—and this very likely explains why MongoDB moved to WiredTiger as the packaged default. However, MMAPv1 has the potential to outperform other storage engines in specific use cases. Ultimately, MMAPv1 may be a good choice for you, if you find its drawbacks tolerable and your particular needs are well suited to highlights its advantages:
These are somewhat narrow scenarios, but if you understand your system and can identify that these advantages are applicable to you, MMAPv1 may very well be worth considering.
- You need to store many separate collections with large documents, for which one or two fields are frequently updated. For example, a database with collections that store metadata for remote sensors that check-in frequently.
- You have a read-only (or mostly read) workload. Even in this case, you still need to ensure that your working set fits into memory.