NoSQL capacity and cost
Size NoSQL deployments with the working shown: Cassandra and ScyllaDB nodes from rows, replication and compaction headroom, with node-replacement and repair times; Redis memory per key by encoding (listpack, intset, hashtable, skiplist) with fragmentation, replication buffers and an eviction policy; MongoDB working set, WiredTiger cache and oplog window; Azure Cosmos DB request units, partitions and cost; and a Zipf hot-partition checker.
Added
Works with
- Cassandra lab Databases and streamingcheck the partition sizes and hot partitions of the table you sized here, in a real data model
- Redis client Databases and streamingconfirm a per-key estimate with MEMORY USAGE and OBJECT ENCODING on a built-in Redis
- MongoDB playground Databases and streamingsee the indexes and documents behind a working-set estimate, with explain plans
- Storage engines: LSM-tree vs B-tree, simulated Distributed systemswatch the compaction whose free-space headroom this page budgets for
- Probabilistic data structures Distributed systemsfind the heavy-hitter keys with a Count-Min sketch before checking their partitions here
- NoSQL data modelling Databases and streamingsize the store a data model was chosen for
How to use it
Five calculators for the questions that come up when a NoSQL store goes into production: how many nodes, how much memory, how many request units, and whether one partition will take more than its share. Each one prints its working line by line — the formula, the numbers that went in and the result — and every assumption is an input you can change. Everything runs in the page.
These are planning estimates. They get the order of magnitude and the shape right (where the headroom goes, which threshold makes memory jump, what a skewed key does) but real numbers come from measuring your own data: the page says which command or metric to check for each.
How to use it
Pick a tab, start from an example (Load an example…) or type your own figures. Number fields
accept 2e10, 20,000,000,000 or 20_000_000_000. Results update when a field changes.
Cassandra / Scylla. Describe the data — rows, rows per partition, the payload of a row, its cells
and the partition key — and the cluster: replication factor, compaction strategy, the operational
headroom you keep, disk per node and the density you are prepared to run. The page works out bytes
on disk, the nodes needed by disk and by density (whichever is more, at least RF, rounded up to a
multiple of racks), the disk of one node as a bar (data, compaction space, headroom, spare), how
long a replacement node takes to stream its data, and whether a full repair cycle finishes inside
gc_grace_seconds. The storage-format overheads are under Storage-format overheads.
Redis memory. Give the number of keys, the key length, the value type and its size. The page
shows what one key costs, piece by piece — hash-table entry, bucket, key string, value in its
encoding, TTL entry — then used_memory, a maxmemory with growth allowance, and the host RAM once
fragmentation, the replication backlog, replica output buffers and the snapshot fork’s
copy-on-write are added. For hashes, sets and sorted sets a chart shows bytes per element against
elements per key, with the jump where the encoding changes. Choose the workload for an eviction
policy.
MongoDB. Documents, their size and the hot fraction; indexes as name:key bytes:hot % (an index on
an increasing value is hot only at its end). The page adds the hot documents and hot index pages
into a working set, works out the RAM whose default WiredTiger cache holds it, and checks a RAM size
you give. Below, writes per second and bytes per change give the oplog window for a given or
default oplog, and the size needed for the window you want.
Cosmos DB RU. Point reads, writes and queries per second, item size, indexing policy and consistency give the RU/s to provision; items and partition-key values give storage, physical partitions and the size and traffic of the largest logical partition; a price per 100 RU/s-hour (editable) gives monthly cost, provisioned and as an autoscale range.
Hot partitions. Choose Zipf(s) over N keys, or paste key counts (one key count per line, from a
sample of your access log). The keys are hashed onto the partitions and the bar chart shows the load
on each, hottest first, against the per-partition limit (Cosmos DB, DynamoDB reads or writes, a
measured Cassandra node, or your own). Salt the K hottest keys into S shards shows what write
sharding does. The advice says whether more partitions can help — they cannot when one key alone is
over the limit.
Formulas and assumptions
Cassandra and ScyllaDB.
-
Bytes per row = payload + row overhead + cells × cell overhead; per partition = key + partition overhead. Defaults: 3 bytes per cell, 6 per row, 32 per partition, for the SSTable format of Cassandra 3.0 and later, which stores timestamps, TTLs and lengths as variable-length deltas (Storage engine). The pre-3.0 rule of thumb was 15 bytes per column and 23 per row; enter that for a conservative figure.
-
On disk = raw × compressed fraction × RF.
-
Free space by compaction strategy (Compaction): size-tiered 50% (a compaction may rewrite the largest tier, in the worst case all the data), leveled 10% (small fixed-size SSTables, ~90% of data in the last level), time-window 1 ÷ windows retained (one window is compacted at a time), at least 5%. Plus the operational headroom you set (snapshots, streaming, growth).
-
Nodes = max(⌈total ÷ usable disk per node⌉, ⌈total ÷ target density⌉, RF), rounded up to a multiple of the racks.
-
Replacing a node streams its data at the aggregate rate you give. Each sender is throttled by
stream_throughput_outbound(24 MiB/s by default in Cassandra 4) and the total by the network and the new node’s disk; with vnodes many nodes send at once. -
A full repair cycle (
nodetool repair -pron every node) reads each node’s primary ranges on its RF replicas at once to build Merkle trees: nodes × (data per node ÷ RF) ÷ validation rate ÷ parallel repairs, plus the out-of-sync fraction streamed. It should finish insidegc_grace_seconds(864,000 s, 10 days, by default), or deleted data can reappear (Repair). -
ScyllaDB uses the same file format family and compaction strategies; it streams and compacts per CPU shard, so the rates and density to enter are higher (ScyllaDB system requirements).
Redis (7.x, 64-bit, the bundled jemalloc built with an 8-byte quantum).
-
Per key: a 24-byte
dictEntry; one 8-byte bucket pointer per slot, slots being the next power of two at or above the key count; the key as an SDS string (3-byte header below 256 bytes, 5 below 64 KB, plus the bytes and a NUL); the value; and for keys with a TTL a second entry in the expires dict. Every allocation is rounded up to a jemalloc size class (8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160…). This gives 56 bytes for an empty key with an empty value, asMEMORY USAGEreports. -
Strings: integers are kept in the object (0–9999 are shared); up to 44 bytes,
embstr(object and string in one allocation); longer,raw(a 16-byte object plus the SDS). -
Collections (Memory optimisation; defaults from
redis.conf): a hash stays a listpack while it has at most 128 fields (hash-max-listpack-entries) and every field and value is at most 64 bytes (hash-max-listpack-value); a sorted set the same (zset-max-listpack-entries 128,zset-max-listpack-value 64); a set of integers is an intset up to 512 members (set-max-intset-entries), otherwise a listpack up to 128 members of up to 64 bytes (set-max-listpack-entries, Redis 7.2); lists are quicklists of listpacks of up to 8 KB (list-max-listpack-size -2). A listpack entry is a 1–5-byte header, the bytes and a 1–5-byte back-length; a listpack has a 6-byte header and an end byte. Past the limits: a hashtable (a dict entry, bucket and SDS strings per element) or, for a sorted set, a skiplist plus a dict (a node of 24 + 16 bytes per level, levels drawn with p = ¼). Sorted-set scores in a listpack are taken as 5-byte integers. -
used_memoryadds ~1 MB for an empty instance. RSS ≈ used ×mem_fragmentation_ratio(INFO memory). -
Host RAM ≥ maxmemory × fragmentation +
repl-backlog-size+ replicas × 256 MB (the hard limit ofclient-output-buffer-limit replica 256mb 64mb 60) + the pages rewritten while a forked snapshot or AOF rewrite runs (copy-on-write). -
Eviction policies (Key eviction):
allkeys-lrufor a pure cache,allkeys-lfufor a skewed long-lived hot set,volatile-lruwhen cache keys with TTLs share the instance with keys that must stay,volatile-ttlwhen TTLs say what is worth keeping,noevictionfor a primary store, queue or locks.
MongoDB (WiredTiger).
-
Working set = documents × size × hot fraction + Σ indexes × hot fraction, with an index taken as documents × (key bytes + 12 bytes for the RecordId and key format). The cache holds pages uncompressed, so the uncompressed (BSON) size is used. Prefix compression often makes real indexes smaller: check
db.collection.stats().indexSizes. -
WiredTiger’s default cache is the larger of 50% of (RAM − 1 GB) and 256 MB (WiredTiger storage engine), so RAM ≥ 2 × working set + 1 GB, plus about 1 MB per connection. The rest of RAM serves the filesystem cache of compressed data.
-
The default oplog is 5% of free disk, at least 990 MB and at most 50 GB, on 64-bit Linux and Windows (Replica set oplog). Each write adds its change plus ~100 bytes of entry fields; window = oplog size ÷ bytes per hour.
Azure Cosmos DB (NoSQL API) — estimates.
-
Microsoft’s published examples (Request Units in Azure Cosmos DB): a point read costs 1 RU at 1 KB, 1.3 RU at 4 KB and 10 RU at 64 KB; a write about 5, 7 and 48 RU, with the default policy of indexing every property. Between those sizes the page interpolates on log(size); above 64 KB in proportion.
-
Strong and bounded-staleness reads cost twice as much (Consistency levels).
-
Writes scale with the number of indexed properties (Indexing policies); the page’s factors for selective (× 0.75) and no indexing (× 0.6) are rough assumptions — measure.
-
Query RU depend on the query: measure the
x-ms-request-chargeheader and enter it. -
A physical partition serves up to 10,000 RU/s and 50 GB; a logical partition (one partition-key value) holds at most 20 GB (Partitioning). Index storage is taken as 20% of item bytes.
-
Cost: RU/s ÷ 100 × the price per 100 RU/s-hour × 730 hours × regions. The default price ($0.008) is a placeholder for standard provisioned throughput: use your region’s price. Autoscale bills the highest RU/s of each hour at 1.5× the rate, between 10% and 100% of the maximum. Check the result with the Cosmos DB capacity calculator.
Hot partitions.
-
Zipf(s): the key of rank i gets 1/is ÷ H(N, s), where H(N, s) = Σ 1/is; so the top key’s share is 1 ÷ H(N, s). Keys are hashed with FNV-1a and the murmur3 finaliser.
-
Per-partition limits: Cosmos DB 10,000 RU/s per physical partition; DynamoDB 3,000 read and 1,000 write capacity units per partition (partition key design); Cassandra has no fixed limit — a partition is served by its RF replicas, so enter what one node sustains for you (20,000 ops/s is only a placeholder).
-
The shards a hot key needs = ⌈key load ÷ limit⌉.
Limits
-
No growth over time: enter the data size at the horizon you are planning for.
-
Cassandra: no materialised views or secondary indexes (add their tables as rows), no tombstone accumulation, a single data centre; ScyllaDB’s incremental compaction strategy is not modelled.
-
Redis: no modules, streams or Redis Cluster slot overhead; the value is the same for every key; listpack integers other than sorted-set scores are treated as strings; Redis 7.0 still calls listpacks ziplists in some configuration names, and Valkey 8’s embedded keys are smaller than this.
-
MongoDB: no sharded-cluster config servers or mongos; the hot fraction is your judgement — check the cache’s
pages read into cacheand eviction counters. -
Cosmos DB: NoSQL API only; no serverless, reserved capacity or multi-region-write pricing differences; RU figures are published examples, not a model of the engine.
-
The hot-partition checker assumes partitions are fixed and keys hashed; range-partitioned stores (Bigtable, HBase) split hot ranges, but a single hot row still cannot be split.
Linking to an example
Links can be written by hand. Add the tab after #: cassandra (the default), redis, mongodb,
cosmos or hot; an example opens its own tab. A link’s settings replace this page’s saved
settings (a button restores yours); settings not in the link take their defaults, and parameters
given with an example override it.
| Parameter | Meaning | Example |
|---|---|---|
|
A preset (list below) |
|
|
|
|
|
Rows, rows per partition |
|
|
Row payload bytes, cells per row, partition-key bytes |
|
|
Overhead bytes per cell, row, partition |
|
|
Compressed size, % of raw |
|
|
Replication factor |
|
|
Compaction |
|
|
Operational headroom, % |
|
|
Disk and target data per node, GiB |
|
|
Racks |
|
|
Streaming and repair-validation rates, MB/s |
|
|
% out of sync at repair; repairs in parallel |
|
|
Redis keys, key length |
|
|
|
|
|
Elements per key, element (or value) length, integers ( |
|
|
Keys with a TTL, % |
|
|
Fragmentation ratio, replicas, backlog MiB |
|
|
Copy-on-write %, growth % |
|
|
|
|
|
Documents, bytes each, hot % |
|
|
Indexes, |
|
|
Connections, RAM per node GiB |
|
|
Writes per second, bytes per change |
|
|
Oplog GB (0 = default), free disk GB, target window h |
|
|
Cosmos point reads, writes, queries per second; RU per query |
|
|
Item size, KB |
|
|
|
|
|
|
|
|
Headroom %, regions |
|
|
Items, partition-key values, largest key’s share % |
|
|
$ per 100 RU/s-hour, $ per GB-month |
|
|
|
|
|
Keys and Zipf exponent |
|
|
Pasted |
|
|
Partitions, total throughput |
|
|
|
|
|
Salt the K hottest keys into S shards |
|
example= |
Shows |
|---|---|
|
Sensor readings: 200 billion small rows, time-window compaction, RF 3 over three racks (open) |
|
The same data under size-tiered compaction: half of every disk kept free (open) |
|
ScyllaDB: denser nodes and faster streaming (open) |
|
A session cache: 20 million hashes of 12 small fields, each with a TTL (open) |
|
The same hashes with 129 fields: past |
|
Leaderboards: 50,000 sorted sets of 1,000 members (open) |
|
A product catalogue: 50 million 2 KB documents, a fifth of them hot (open) |
|
Heavy writes into a default oplog: a window of hours, not days (open) |
|
E-commerce orders: 2,000 point reads and 300 writes a second on 2 KB items (open) |
|
Partitioned by tenant, one tenant with 40% of the data: past 20 GB per logical partition (open) |
|
A celebrity key: Zipf s = 1.2 over a million users on 32 partitions (open) |
|
The same with the ten hottest keys split into 8 write shards each (open) |
|
A near-uniform key (s = 0.2): every partition near the mean (open) |
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser; nothing you type leaves this page.