Upserts in a high-write collection with multiple indexes create amplified write overhead due to mandatory index maintenance on both read and write paths, with performance degradation scaling non-linearly as the number of indexes increases.
In a high-write MongoDB collection, an upsert (update with upsert: true) on a collection with multiple indexes creates significant performance implications beyond a simple insert or update. Every index on a collection adds write overhead because MongoDB must update each index whenever a document is inserted or modified [citation:2]. For an upsert specifically, the operation must first query the collection using the filter (which may require index traversal), determine whether to update an existing document or insert a new one, and then perform the corresponding write operation across all indexes. This dual read-write nature makes upserts particularly expensive in multi-index scenarios [citation:8].
Each index on a collection acts as an additional data structure that must be kept consistent with the primary data. For a collection with N indexes, an upsert that results in an insert must add entries to all N indexes [citation:2]. For an upsert that results in an update, only indexes on fields that actually changed need updating, but the query phase still requires index access to locate the document [citation:8]. This means write amplification grows linearly with the number of indexes. The MongoDB documentation explicitly states: 'Each index on a collection adds some overhead to the performance of write operations' [citation:2].
Unique index validation: Each unique index requires MongoDB to check for existing values across the entire collection before completing an upsert that would insert a new document. This validation adds significant overhead, especially as the collection grows [citation:1].
Sparse and partial indexes: These specialized indexes can actually increase complexity because they require additional logic to determine whether a document belongs in the index at all [citation:4].
Index key generation: For insert operations resulting from upserts, MongoDB must generate and store index keys for every indexed field. This CPU and I/O cost multiplies with each additional index.
B-tree maintenance: Each index is a B-tree structure that must be balanced and potentially split as new entries are added. Multiple indexes mean multiple B-tree operations per write.
Upsert performance degrades noticeably as collections grow large, particularly when multiple indexes are involved. Community-reported cases show that while upserts perform well on collections with thousands of documents, performance can collapse when collections reach millions of documents—even with identical indexing strategies [citation:6][citation:10]. This occurs because index tree depth increases with collection size, making each index lookup and insertion more expensive. For high-write scenarios, this means that maintaining acceptable performance requires either scaling hardware or reducing the number of indexes.
In high-write environments, multiple indexes create additional contention points. Each index update requires acquiring locks or coordinating across storage engine transactions. When multiple writers attempt to upsert concurrently, index contention can become a bottleneck. The MongoDB manual notes that indexes must be updated for every write operation, and this overhead becomes particularly pronounced under concurrent load [citation:2]. Unique indexes exacerbate this because they require global uniqueness checks that can serialize operations on the same key values.
Given these impacts, several mitigation strategies emerge: First, index pruning—remove indexes that aren't essential for queries. Second, hash-based filtering—one developer reported reducing CPU usage from 80-90% to 1-5% by replacing multi-field filters with a single hashed value, effectively trading multiple index operations for a single index lookup [citation:10]. Third, batch sizing—larger batches can amortize index overhead, though they also increase memory pressure. Fourth, separate insert and update paths—since upserts are 6-8x slower than inserts, consider attempting inserts first and handling duplicates separately [citation:7]. Fifth, covered queries—design indexes to cover read queries completely, reducing the need for additional indexes [citation:3].
Sparse and partial indexes deserve special mention because they add conditional logic to index maintenance. The MongoDB community forums document a case where switching from a single unique index to two sparse unique indexes caused bulk write performance to collapse from <10% CPU to 100% CPU with timeouts, even on an M60 cluster [citation:4]. This extreme degradation occurred because each upsert had to evaluate whether documents belonged in each sparse index, adding computational overhead far beyond simple B-tree operations. For high-write collections, such conditional indexes should be used with extreme caution.