This blog was published in January of 2021 and updated in April of 2024.
As with any database platform, MongoDB performance is of paramount importance to keeping your application running quickly. In this blog post, we’ll show you five configuration options that can impact the performance for your MongoDB Deployment and will help keep your database fast and performing at its peak.
MongoDB Performance Overview
MongoDB performance consists of several factors; there’s OS Settings, DB Configuration Settings, DB Internal Settings, Memory Settings, and Application Settings. This post is going to focus on the MongoDB database configuration options around performance and how to set them. These options are ones that are set in the database configuration itself and can impact your performance.
MongoDB Configuration Options
So how do we ensure our performance configuration options are enabled or set up correctly? And which ones are the most important? We’ll now go through five configuration options that will help your MongoDB environment be performant!
MongoDB uses a configuration file in the YAML file format. The configuration file is usually found in the following locations, depending on your Operating System:
DEFAULT MongoDB CONFIGURATION FILE
- On Linux, a default /etc/mongod.conf configuration file is included when using a package manager to install MongoDB.
- On Windows, a default <install directory>/bin/mongod.cfg configuration file is included during the installation.
- On macOS, a default /usr/local/etc/mongod.conf configuration file is included when installing from MongoDB’s official Homebrew tap.
storage.wiredTiger.engineConfig.cacheSizeGB
Our first configuration option to help with your MongoDB performance is storage.wiredTiger.engineConfig.cacheSizeGB.
1 2 3 4 | storage: wiredTiger: engineConfig: cacheSizeGB: <value> |
Since MongoDB 3.0, MongoDB has used WiredTiger as its default Storage Engine, so we’ll be examining MongoDB Memory Performance from a WiredTiger perspective. By default, MongoDB will reserve 50% of the available memory – 1 GB for the WiredTiger cache or 256 MB, whichever is greater. For example, a system with 16 GB of RAM would have a WiredTiger cache size of 7.5 GB.
1 | ( 0.5 * (16-1) ) |
The size of this cache is important to ensure WiredTiger is performant. It’s worth taking a look to see if you should alter it from the default. A good rule of thumb is that the size of the cache should be large enough to hold the entire application working set.
Note that if you’re in a containerized environment, you may also need to set the configuration option to 50% – 1 GB of the memory that is available to the container. MongoDB may adhere to the container’s memory limits or it may get the host’s memory limit, depending on how the system call is returned when MongoDB asks. You can verify what MongoDB believes the memory limit is by running:
1 | db.hostInfo() |
And checking the hostinfo.system.memLimitMB value. This is available from MongoDB 3.6.13+ and MongoDB 4.0.9+ forward.
How do we know whether to increase or decrease our cache size? Look at the cache usage statistics:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | db.serverStatus().wiredTiger.cache { "application threads page read from disk to cache count" : 9, "application threads page read from disk to cache time (usecs)" : 17555, "application threads page write from cache to disk count" : 1820, "application threads page write from cache to disk time (usecs)" : 1052322, "bytes allocated for updates" : 20043, "bytes belonging to page images in the cache" : 46742, "bytes belonging to the history store table in the cache" : 173, "bytes currently in the cache" : 73044, "bytes dirty in the cache cumulative" : 38638327, "bytes not belonging to page images in the cache" : 26302, "bytes read into cache" : 43280, "bytes written from cache" : 20517382, "cache overflow score" : 0, "checkpoint blocked page eviction" : 0, "eviction calls to get a page" : 5973, "eviction calls to get a page found queue empty" : 4973, "eviction calls to get a page found queue empty after locking" : 20, "eviction currently operating in aggressive mode" : 0, "eviction empty score" : 0, "eviction passes of a file" : 0, "eviction server candidate queue empty when topping up" : 0, "eviction server candidate queue not empty when topping up" : 0, "eviction server evicting pages" : 0, "eviction server slept, because we did not make progress with eviction" : 735, "eviction server unable to reach eviction goal" : 0, "eviction server waiting for a leaf page" : 2, "eviction state" : 64, "eviction walk target pages histogram - 0-9" : 0, "eviction walk target pages histogram - 10-31" : 0, "eviction walk target pages histogram - 128 and higher" : 0, "eviction walk target pages histogram - 32-63" : 0, "eviction walk target pages histogram - 64-128" : 0, "eviction walk target strategy both clean and dirty pages" : 0, "eviction walk target strategy only clean pages" : 0, "eviction walk target strategy only dirty pages" : 0, "eviction walks abandoned" : 0, "eviction walks gave up because they restarted their walk twice" : 0, "eviction walks gave up because they saw too many pages and found no candidates" : 0, "eviction walks gave up because they saw too many pages and found too few candidates" : 0, "eviction walks reached end of tree" : 0, "eviction walks started from root of tree" : 0, "eviction walks started from saved location in tree" : 0, "eviction worker thread active" : 4, "eviction worker thread created" : 0, "eviction worker thread evicting pages" : 902, "eviction worker thread removed" : 0, "eviction worker thread stable number" : 0, "files with active eviction walks" : 0, "files with new eviction walks started" : 0, "force re-tuning of eviction workers once in a while" : 0, "forced eviction - history store pages failed to evict while session has history store cursor open" : 0, "forced eviction - history store pages selected while session has history store cursor open" : 0, "forced eviction - history store pages successfully evicted while session has history store cursor open" : 0, "forced eviction - pages evicted that were clean count" : 0, "forced eviction - pages evicted that were clean time (usecs)" : 0, "forced eviction - pages evicted that were dirty count" : 0, "forced eviction - pages evicted that were dirty time (usecs)" : 0, "forced eviction - pages selected because of too many deleted items count" : 0, "forced eviction - pages selected count" : 0, "forced eviction - pages selected unable to be evicted count" : 0, "forced eviction - pages selected unable to be evicted time" : 0, "forced eviction - session returned rollback error while force evicting due to being oldest" : 0, "hazard pointer blocked page eviction" : 0, "hazard pointer check calls" : 902, "hazard pointer check entries walked" : 25, "hazard pointer maximum array length" : 1, "history store key truncation calls that returned restart" : 0, "history store key truncation due to mixed timestamps" : 0, "history store key truncation due to the key being removed from the data page" : 0, "history store score" : 0, "history store table insert calls" : 0, "history store table insert calls that returned restart" : 0, "history store table max on-disk size" : 0, "history store table on-disk size" : 0, "history store table out-of-order resolved updates that lose their durable timestamp" : 0, "history store table out-of-order updates that were fixed up by moving existing records" : 0, "history store table out-of-order updates that were fixed up during insertion" : 0, "history store table reads" : 0, "history store table reads missed" : 0, "history store table reads requiring squashed modifies" : 0, "history store table remove calls due to key truncation" : 0, "history store table writes requiring squashed modifies" : 0, "in-memory page passed criteria to be split" : 0, "in-memory page splits" : 0, "internal pages evicted" : 0, "internal pages queued for eviction" : 0, "internal pages seen by eviction walk" : 0, "internal pages seen by eviction walk that are already queued" : 0, "internal pages split during eviction" : 0, "leaf pages split during eviction" : 0, "maximum bytes configured" : 8053063680, "maximum page size at eviction" : 376, "modified pages evicted" : 902, "modified pages evicted by application threads" : 0, "operations timed out waiting for space in cache" : 0, "overflow pages read into cache" : 0, "page split during eviction deepened the tree" : 0, "page written requiring history store records" : 0, "pages currently held in the cache" : 24, "pages evicted by application threads" : 0, "pages queued for eviction" : 0, "pages queued for eviction post lru sorting" : 0, "pages queued for urgent eviction" : 902, "pages queued for urgent eviction during walk" : 0, "pages read into cache" : 20, "pages read into cache after truncate" : 902, "pages read into cache after truncate in prepare state" : 0, "pages requested from the cache" : 33134, "pages seen by eviction walk" : 0, "pages seen by eviction walk that are already queued" : 0, "pages selected for eviction unable to be evicted" : 0, "pages selected for eviction unable to be evicted as the parent page has overflow items" : 0, "pages selected for eviction unable to be evicted because of active children on an internal page" : 0, "pages selected for eviction unable to be evicted because of failure in reconciliation" : 0, "pages walked for eviction" : 0, "pages written from cache" : 1822, "pages written requiring in-memory restoration" : 0, "percentage overhead" : 8, "tracked bytes belonging to internal pages in the cache" : 5136, "tracked bytes belonging to leaf pages in the cache" : 67908, "tracked dirty bytes in the cache" : 493, "tracked dirty pages in the cache" : 1, "unmodified pages evicted" : 0 } |
There’s a lot of data here about WiredTiger’s cache, but we can focus on the following fields:
- wiredTiger.cache.maximum bytes configured: This is the maximum cache size.
- wiredTiger.cache.bytes currently in the cache – This is the size of the data currently in the cache. This should not be greater than the maximum bytes configured.
- wiredTiger.cache.tracked dirty bytes in the cache – This is the size of the dirty data in the cache. This value should be less than the bytes currently in the cache value.
Looking at the above values, we can determine if we need to up the size of the WiredTiger cache for our instance. Additionally, we can look at the wiredTiger.cache.pages read into cache value for read-heavy applications. If this value is consistently high, increasing the cache size may improve overall read performance.
storage.wiredTiger.engineConfig.directoryForIndexes
Our second configuration option is storage.wiredTiger.engineConfig.directoryForIndexes.
1 2 3 4 | storage: wiredTiger: engineConfig: directoryForIndexes: <true or false> |
Setting this value to true creates two directories in your storage.dbPath directory, one named collection which will hold your collection data files, and another one named index which will hold your index data files. This allows you to create separate storage volumes for collections and indexes if you wish, which can spread the amount of disk I/O across each volume, but with most modern storage options you can get the same performance benefits by just striping your disk across two volumes (RAID 0). This can help separate index I/O from collection-based I/O and reduce storage based latencies, although index-based I/O is unlikely to be costly due to its smaller size.
Further Reading: A Comprehensive Guide on MongoDB Indexes to Better Performance
storage.wiredTiger.collectionConfig.blockCompressor
Our third configuration option is storage.wiredTiger.collectionConfig.blockCompressor.
1 2 3 4 | storage: wiredTiger: collectionConfig: blockCompressor: <value> |
This option sets the compression options for all of your collection data. Possible values for this parameter are none, snappy (default), zlib, and zstd. So how does compression help your performance? The WiredTiger cache generally stores changes uncompressed, with the exception of some very large documents. Now we need to write that uncompressed data to disk.
Compression Types:
Snappy compression is fairly straightforward, snappy compression gathers your data up to a maximum of 32KB, compresses your data, and if compression is successful, writes the block rounded up to the nearest 4KB.
Zlib compression works a little differently; it will gather more data and compress enough to fill a 32KB block on disk. This is more CPU-intensive but generally results in better compression ratios (independent of the inherent differences between snappy and zlib).
Zstd is a newer compression algorithm, developed by Facebook that offers improvements over zlib (better compression rates, less CPU intensive, faster performance).
Which compression algorithm to choose depends greatly on your workload. For most write-heavy workloads, snappy compression will perform better than zlib and zstd but will require more disk space. For read-heavy workloads, zstd is often the best choice because of its better decompression rates.
storage.directoryPerDB
Another configuration option to help with MongoDB performance is storage.directoryPerDB.
1 2 | storage: directoryPerDB: <true or false> |
Similar to the above configuration file option, storage.wiredTiger.engineConfig.directoryForIndexes, setting this value to true creates a separate directory in your storage.dbPath for each database in your MongoDB instance. This allows you to create separate storage volumes for each database if you wish, which can spread the amount of disk I/O across each volume. This can help when you have multiple databases with intensive I/O needs. Additionally, if you use this parameter in tandem with storage.wiredTiger.engineConfig.directorForIndexes, your directory structure will look like this:
1 2 3 | -Database_name -Collection -Index |
net.compression.compressors
Our final configuration option that can help keep your database performant is the net.compression.compressors configuration option.
1 2 3 | net: compression: compressors: <value> |
This option allows you to compress the network traffic between your mongos, mongod, and even your mongo shell. There are currently three types of compression available, snappy, zlib, and zstd. Since MongoDB 3.6, compression has been enabled by default. In MongoDB 3.6 and 4.0, snappy was the default. Since MongoDB 4.2, the default is now snappy, zstd, and zlib compressors, in that order. It’s also important to note that you must have at least one mutual compressor on each side of your network conversation for compression to happen. For example, if your shell uses zlib compression but you have your mongod set to only accept snappy compression, then no compression will occur between the two. If both accept zstd compression, then zstd compression would be used between them. When compression is set it can be very helpful in reducing replication lag and overall reducing network latency as the size of the data moving across the network is decreased, sometimes dramatically. In cloud environments, setting this configuration option can also lead to decreased data transfer costs.
Elevate Your MongoDB Database Performance with Percona
In this blog post, we’ve gone over five MongoDB configuration options to ensure you have a more performant MongoDB deployment. We hope that these configuration options will help you build more performant MongoDB deployments and avoid slowdowns and bottlenecks. Thanks for reading!
Our MongoDB Performance Tuning eBook is a collection of insights, strategies, and best practices from Percona’s MongoDB experts. Use it to diagnose — and correct — the issues that may be affecting your database’s performance.
Download now: Master MongoDB Performance
FAQs
What are the top performance-related configuration options in MongoDB?
The top performance-related configuration options in MongoDB include:
- storage.wiredTiger.engineConfig.cacheSizeGB, which specifies the amount of RAM for WiredTiger to use for data caching. This setting is crucial for optimizing data retrieval and overall database performance.
- net.maxIncomingConnections, which determines the maximum number of simultaneous connections the database can handle, directly influencing the database’s ability to manage concurrent users or tasks.
- operationProfiling.mode and operationProfiling.slowOpThresholdMs, which control the operation profiling level and set the threshold for considering operations slow. These settings are vital for monitoring and improving the database’s performance by identifying and addressing slow operations.
How do MongoDB configuration settings affect overall database performance?
Configuration settings in MongoDB directly influence how the database handles workloads. Adjusting parameters related to memory allocation, connection limits, and query profiling can lead to significant improvements in response times and throughput, as well as more efficient resource usage.
What are the key considerations when adjusting MongoDB configuration for optimal performance?
- Assess the workload type (read-heavy, write-heavy, mixed) to tailor configuration appropriately.
- Monitor current performance metrics to establish baselines.
- Prioritize adjustments that address identified bottlenecks without negatively impacting other areas.
How can MongoDB configuration adjustments mitigate potential performance bottlenecks?
Addressing performance bottlenecks involves:
- Increasing cache size to enhance read performance if memory is available.
- Adjusting net.maxIncomingConnections and operationProfiling settings to manage load and identify slow operations.
- Fine-tuning indexes and query patterns based on the operation profiling results to reduce unnecessary workloads on the database.
I have a mongoDB 4.0 deployment which uses snappy compression by default. I use JAVA mongodb driver 3.9. In the driver I don’t explicitly specify any compression method.
Here are my questions:
1. Will there be any compression used between mongodb driver and mongodb?
2. When I make a query, the snappy compressed data is uncompressed and then sent to client (in case of no client compression method), is this correct?
directoryForIndexes not work here. In any way that i try. Do you have any specification about it? Like, does not work with cacheSizeGB ?