MongoDBDocument store
2Won
7Lost
0Parity
Against PostgreSQL, on 9 scenarios, median figures. No overall factor is stated: the spread is the result.
Most database benchmarks measure workloads nobody runs: generate uniform rows, pick flattering queries, call the driver directly, publish a number with an X after it. The number is usually true and almost always useless, because your application does not talk to a driver.
So this one swaps the storage engine underneath a working system and changes nothing else. The data, the queries, the indexes and the code path stay exactly as they are, and every measurement below includes the real cost of query translation, serialization, the access check, the soft-delete filter, the account filter and pagination.
First comparison: MongoDB against ScyllaDB. More engines get added here as they are measured, and the rules never bend for a new one.
The reasoning behind these numbers, the two runs we threw away and what we would tell a team facing this decision:
Stating MongoDB against PostgreSQL, median figures. Narrowing the groups dims rows it does not cover — nothing is removed from the page.
Against PostgreSQL, on 9 scenarios, median figures. No overall factor is stated: the spread is the result.
Reference engine — every factor on this page is stated against it. Its own record above is against the fastest other engine on each of 9 scenarios, median figures.
Against PostgreSQL, on 9 scenarios, median figures. No overall factor is stated: the spread is the result.
Against PostgreSQL, on 9 scenarios, median figures. No overall factor is stated: the spread is the result.
Against PostgreSQL, on 9 scenarios, median figures. No overall factor is stated: the spread is the result.
Against PostgreSQL, on 9 scenarios, median figures. No overall factor is stated: the spread is the result.
Against PostgreSQL, on 8 scenarios, median figures. No overall factor is stated: the spread is the result.
| Scenario | Rows | MongoDB | Postgres | Cockroach | ScyllaDB | Cassandra | CouchDB | FerretDB | Factor | Relative | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Median | p95 | Median | p95 | Median | p95 | Median | p95 | Median | p95 | Median | p95 | Median | p95 | ||||
Bulk insert, 250 000 recordsWrite throughput with six secondary indexes already in place. Writes | 250 000 | 44 238 rows/s | — | 11 155 rows/s | — | 536 rows/s | — | 13 305 rows/s | — | 31 328 rows/s | — | 10 001 rows/s | — | 10 399 rows/s | — | MongoDB 3.97× | |
Point read by identifier, 200 requests200 sequential primary-key lookups, one round trip each. Key access | 200 | 62 ms | 75 ms | 98 ms | 106 ms | 222 ms | 270 ms | 103 ms | 112 ms | 150 ms | 171 ms | 3.06 s | 3.08 s | — | — | MongoDB 1.59× | |
All measurements for one inspectionEvery child record of one parent, on an indexed foreign key. Key access | 494 | 3.8 ms | 4.2 ms | 1.9 ms | 2.5 ms | 11 ms | 12 ms | 4.6 ms | 5.3 ms | 8.9 ms | 24 ms | 261 ms | 266 ms | 16.7 s | 18.8 s | Postgres 2.01× | |
First sorted page, 50 rowsPage one of the collection, sorted by creation date. Pagination | 50 | 1.6 ms | 1.7 ms | 1.1 ms | 1.3 ms | 9.5 ms | 11 ms | 9.7 ms | 11 ms | 16 ms | 17 ms | 251 ms | 263 ms | 20.5 s | 20.7 s | Postgres 1.45× | |
Deep page, page 200Skip 9 950 rows, return the next 50. What a numbered pager asks for. Pagination | 50 | 5.5 ms | 6.1 ms | 4.6 ms | 5.1 ms | 11 ms | 15 ms | 5.15 s | 5.20 s | 1.57 s | 1.66 s | 30.0 s | 30.7 s | 2.50 s | 2.56 s | Postgres 1.18× | |
Filtered scan on an unindexed booleanEvery record where an approval flag is false. Indexed on neither engine. Scans and filters | 10 000 | 72 ms | 104 ms | 35 ms | 41 ms | 39 ms | 55 ms | 5.09 s | 5.13 s | 1.49 s | 1.54 s | 27.7 s | 28.5 s | 2.29 s | 2.32 s | Postgres 2.05× | |
Range scan on an unindexed numberOne indexed equality plus a greater-than on an unindexed measurement value. Scans and filters | 165 | 2.5 ms | 2.9 ms | 1.4 ms | 1.4 ms | 10 ms | 13 ms | 3.9 ms | 4.4 ms | 7.5 ms | 8.2 ms | 252 ms | 261 ms | 16.8 s | 17.3 s | Postgres 1.76× | |
Exact count of the whole collectionHow many records are there. Not an estimate. Whole-collection work | 250 006 | 72 ms | 73 ms | 26 ms | 28 ms | 169 ms | 214 ms | 5.11 s | 5.15 s | 1.49 s | 1.53 s | 29.8 s | 34.0 s | 11.6 s | 11.9 s | row counts differ | |
Load the whole collectionEvery record, deserialized into the application. Whole-collection work | 250 006 | 1.55 s | 1.64 s | 522 ms | 653 ms | 531 ms | 688 ms | 5.12 s | 5.21 s | 1.50 s | 1.54 s | 29.7 s | 29.9 s | 25.8 s | 26.2 s | row counts differ | |
Against PostgreSQL, on 0 scenarios, median figures. No overall factor is stated: the spread is the result.
Against PostgreSQL, on 0 scenarios, median figures. No overall factor is stated: the spread is the result.
| Scenario | Rows | MongoDB | ScyllaDB | Factor | Relative | ||
|---|---|---|---|---|---|---|---|
| Median | p95 | Median | p95 | ||||
Bulk insert, 250 000 recordsWrite throughput with six secondary indexes already in place. Writes | 250 000 | 11 028 rows/s | — | 17 147 rows/s | — | — | |
Point read by identifier, 200 requests200 sequential primary-key lookups, one round trip each. Key access | 200 | 68 ms | 83 ms | 115 ms | 122 ms | — | |
All measurements for one inspectionEvery child record of one parent, on an indexed foreign key. Key access | 494 | 4.5 ms | 6.3 ms | 6.1 ms | 6.9 ms | — | |
First sorted page, 50 rowsPage one of the collection, sorted by creation date. Pagination | 50 | 1.9 ms | 2.5 ms | 12 ms | 13 ms | — | |
Deep page, page 200Skip 9 950 rows, return the next 50. What a numbered pager asks for. Pagination | 50 | 6.0 ms | 6.5 ms | 6.74 s | 9.63 s | — | |
Filtered scan on an unindexed booleanEvery record where an approval flag is false. Indexed on neither engine. Scans and filters | 10 000 | 80 ms | 147 ms | 6.23 s | 6.56 s | — | |
Range scan on an unindexed numberOne indexed equality plus a greater-than on an unindexed measurement value. Scans and filters | 165 | 2.5 ms | 3.7 ms | 4.9 ms | 5.7 ms | — | |
Exact count of the whole collectionHow many records are there. Not an estimate. Whole-collection work | 250 006 | 76 ms | 78 ms | 6.34 s | 7.93 s | — | |
Load the whole collectionEvery record, deserialized into the application. Whole-collection work | 250 006 | 1.95 s | 2.38 s | 6.43 s | 7.15 s | — | |
| Scenario | Rows | MongoDB | ScyllaDB | Factor | Relative | ||
|---|---|---|---|---|---|---|---|
| Median | p95 | Median | p95 | ||||
Point read by identifier, 200 requests200 sequential primary-key lookups, one round trip each. Key access | 200 | 56 ms | 66 ms | 116 ms | 119 ms | — | |
All measurements for one inspectionEvery child record of one parent, on an indexed foreign key. Key access | 494 | 4.5 ms | 5.2 ms | 4.9 ms | 6.8 ms | — | |
First sorted page, 50 rowsPage one of the collection, sorted by creation date. Pagination | 50 | 1.6 ms | 2.2 ms | 9.7 ms | 21 ms | — | |
Deep page, page 200Skip 9 950 rows, return the next 50. What a numbered pager asks for. Pagination | 50 | 5.1 ms | 6.0 ms | 5.51 s | 5.62 s | — | |
Filtered scan on an unindexed booleanEvery record where an approval flag is false. Indexed on neither engine. Scans and filters | 10 000 | 81 ms | 89 ms | 5.44 s | 5.54 s | — | |
Range scan on an unindexed numberOne indexed equality plus a greater-than on an unindexed measurement value. Scans and filters | 165 | 2.8 ms | 8.4 ms | 4.6 ms | 6.3 ms | — | |
Exact count of the whole collectionHow many records are there. Not an estimate. Whole-collection work | 250 006 | 66 ms | 68 ms | 5.45 s | 5.47 s | — | |
Load the whole collectionEvery record, deserialized into the application. Whole-collection work | 250 006 | 1.99 s | 2.50 s | 5.48 s | 5.56 s | — | |
| Scenario | Rows | MongoDB | ScyllaDB | Factor | Relative | ||
|---|---|---|---|---|---|---|---|
| Median | p95 | Median | p95 | ||||
Bulk insert, 250 000 recordsWrite throughput with six secondary indexes already in place. Writes | 250 000 | 27 105 rows/s | — | 21 077 rows/s | — | — | |
Point read by identifier, 200 requests200 sequential primary-key lookups, one round trip each. Key access | 200 | 51 ms | 61 ms | 114 ms | 124 ms | — | |
All measurements for one inspectionEvery child record of one parent, on an indexed foreign key. Key access | 494 | 88 ms | 93 ms | 5.2 ms | 6.5 ms | — | |
First sorted page, 50 rowsPage one of the collection, sorted by creation date. Pagination | 50 | 86 ms | 88 ms | 9.4 ms | 18 ms | — | |
Deep page, page 200Skip 9 950 rows, return the next 50. What a numbered pager asks for. Pagination | 50 | 5.9 ms | 6.6 ms | 6.12 s | 20.8 s | — | |
Filtered scan on an unindexed booleanEvery record where an approval flag is false. Indexed on neither engine. Scans and filters | 10 000 | 85 ms | 93 ms | 5.76 s | 7.36 s | — | |
Range scan on an unindexed numberOne indexed equality plus a greater-than on an unindexed measurement value. Scans and filters | 165 | 94 ms | 103 ms | 4.8 ms | 8.1 ms | — | |
Exact count of the whole collectionHow many records are there. Not an estimate. Whole-collection work | 250 006 | 75 ms | 78 ms | 5.61 s | 6.01 s | — | |
Load the whole collectionEvery record, deserialized into the application. Whole-collection work | 250 006 | 2.30 s | 2.85 s | 5.63 s | 5.94 s | — | |
PostgreSQL, Cassandra, Couchbase, CockroachDB, CouchDB and FerretDB can all go through the same harness. These eight rules stay fixed, because the moment one of them bends for a single engine the whole comparison is worthless.