skip to content

How do you size a bloom filter for a target false-positive rate, and what does tightening it cost?

level: seniorimportance: should knowfreq 38%

answer

  1. the rate is an input, not an outcome
  2. cost per element, not per byte of key
  3. how many bits buy one order of magnitude
  4. about ten bits per element for one percent
  5. hash count near 0.7 times bits per element

basics

~20 s

Size from the target rate and the final element count: roughly 1.44 times log2(1/rate) bits per element, with the hash count near 0.7 times that. About 10 bits per element buys 1%; each further factor of 100 costs only about twice the memory.

solid answer

~50 s

Two inputs drive everything: `n`, the number of elements you will *end up* holding, and the acceptable false-positive rate. Bits per element works out to about `1.44 * log2(1/rate)` when the hash count is optimal, and the optimal hash count is about `0.69` times the bits per element. So 1% needs roughly 10 bits per element with about 7 hash functions, and 0.01% needs roughly 19 bits with about 13 — a hundredfold tighter error for a bit under twice the memory, which is the property that makes these filters so effective. Two disciplines follow. Pick the rate from the cost of the fallback path, not by feel: the expected extra work is the rate times the volume of queries that would otherwise have been answered 'absent'. And size for the final `n`, because inserting past the design load raises the rate steeply and you cannot resize in place — the filter never kept the keys to re-hash.

go deeper

for a junior

Know that both the array size and the number of hash functions are chosen up front, and that a smaller error rate simply costs more bits per element. You are not expected to derive the formula.

for a middle

Be able to quote the shape: roughly ten bits per element for one percent, with the hash count around seven, and each additional factor of ten in accuracy costing a few more bits. Explain that the rate rises as the filter fills.

for a senior

Derive the sizing from a real workload: final element count, the cost of one authoritative check, and the traffic mix. Explain why overfilling degrades the rate silently, why in-place resizing is impossible, and what you would monitor to catch drift.

for a principal

Own the error target as a budget negotiation. Argue the rate from what the fallback path costs at peak, decide whether memory across the fleet or latency on confirmations is the binding constraint, and commit to a rebuild pipeline before shipping the filter.

## The inputs Sizing needs exactly two numbers you must get from the problem, not from the structure: how many distinct elements the filter will hold at its fullest, and how often you can tolerate a positive that turns out to be wrong. Everything else is arithmetic. With `m` bits, `n` elements and `k` hash functions, the false-positive rate is approximately `(1 - e^(-kn/m))^k`. Minimising over `k` gives two facts worth memorising: - optimal hash count: `k = (m/n) * ln 2`, about `0.69` per bit of budget per element; - required space at that optimum: `m/n = log2(1/rate) / ln 2`, about `1.44 * log2(1/rate)` bits per element. | Target rate | Bits per element | Hash count | | --- | --- | --- | | 10% | ~4.8 | ~3 | | 1% | ~9.6 | ~7 | | 0.1% | ~14.4 | ~10 | | 0.01% | ~19.2 | ~13 | ## Reading the table properly The striking line is the growth: each factor of ten in accuracy costs a flat ~4.8 bits per element. Going from 1% to 0.01% — a hundredfold improvement — barely doubles the memory. Candidates who expect a hundredfold cost for a hundredfold gain have the wrong mental model, and it leads them to accept far sloppier rates than they need to. Make it concrete with a pre-check that screens submitted passwords against a large set of known-breached credentials before an authoritative service call. At 500 million entries, 1% costs about `9.6 * 5e8` bits ≈ 600 MB; 0.01% costs about 1.2 GB. Meanwhile an exact set must store the key material itself — say a 20-byte digest per entry — plus per-entry structural overhead, which lands in the tens of gigabytes. Note also that the filter's cost is unchanged if the entries are long passphrases rather than short digests, because only hash outputs index the array. The exact set's cost is not. ## Choosing the rate from the fallback, not from taste The rate is a business number. The expected extra work per unit time is `rate * (queries that would otherwise be answered 'absent') * cost of one authoritative check` If the confirmation is a local disk read and 99% of traffic is misses, 1% is comfortably cheap. If the confirmation is a cross-region service call with a latency budget attached, or if the fallback path is what saturates first under load, you buy the extra five bits per element and take 0.1%. And if a false positive is not confirmable at all — because the positive *is* the decision — then no rate is small enough and the structure is the wrong choice regardless of budget. One subtlety worth stating: the rate applies to queries for elements that are *not* in the set. If your traffic is mostly hits, the filter is doing little work for you anyway, since its value comes entirely from the exact negatives. ## Sizing for the final n, and what happens when you do not The formula's `n` is the count at the filter's fullest, not today's count. Suppose you sized for 1% at 9.6 bits per element and then inserted twice as many elements. The bits per element halve to about 4.8 while the hash count stays at 7 — badly suboptimal for the new density — and the rate climbs from 1% to well over 10%. Nothing errors, nothing logs; the filter simply becomes an expensive way to send most queries down the fallback path, and the first symptom is usually load on the system the filter was protecting. You cannot fix this in place. Enlarging the array would require re-hashing every element into the new positions, and the filter never stored the elements. The available moves are: rebuild from the authoritative source at a larger size; keep a chain of filters, adding a new, larger one when the current fills, so a query checks each in turn at the cost of extra probes and a compounded rate; or partition the key space up front and size each partition's filter separately. Under-sizing `n` is the expensive mistake; over-sizing merely wastes memory and is safe. ## Operating one Treat the design rate as an SLO with a measurement behind it. If you can observe how often a positive fails its authoritative check, you have the observed rate directly; compare it to the design target and alarm on drift, which is the earliest signal that the element count has outgrown the sizing. Also record `n`, `m` and `k` alongside the artefact, because a filter with no recorded parameters is a filter nobody can safely resize, rebuild or reason about later. Finally, keep the hash count sane. It is tempting to raise `k` for accuracy, but past the optimum it makes the rate *worse* while adding probe cost to every query. Compute it from the budget rather than picking a round number.

  • What happens if you insert twice the element count you sized for?
    Bits per element halve while the hash count stays tuned for the old density, and the false-positive rate climbs from around 1% to well over 10%. Nothing fails loudly — the filter just stops removing work, and the first symptom is unexpected load on whatever it was protecting.
  • Why is bits-per-element independent of how long the keys are?
    Only hash outputs index the array, so a two-hundred-character address and an eight-byte identifier each set the same number of bits. That is the memory argument in one sentence: an exact set's footprint grows with key length and count, a filter's grows only with count.
  • How do you pick the target rate rather than guessing it?
    From the fallback's cost: expected extra work equals the rate times the volume of queries that would otherwise be answered absent, times the cost of one authoritative check. A cheap local confirmation tolerates 1%; a cross-region call or a tight latency budget justifies buying the extra bits for 0.1%.
  • Can you enlarge an existing filter to restore its target rate?
    Not in place. Growing the array changes every position, and re-hashing requires the elements, which were never stored. The options are rebuilding at a larger size from the authoritative source, or chaining a new larger filter alongside the full one and querying both, which costs extra probes and compounds the rate.

saying these in an interview costs you the question

  • Sizes for today's element count instead of the final one
  • Assumes a hundredfold tighter rate costs a hundredfold memory
  • Picks the error rate by feel with no fallback cost model
  • Raises the hash count for accuracy past the optimum
  • Believes the array can be resized in place later

context