SD System Design In Depth
0%
lesson system-designdata-structuresprobabilistic

Bloom filters

Module 2.2: NoSQL Architectures, Partitioning & IDs Updated Sep 17, 2026
📄 Cheat Sheet
What Are Bloom Filters?
Spanning Tree What Are Bloom Filters? Watch on YouTube ↗

Animated build-up of the bit array and k hashes, with an intuitive derivation of why false positives happen and false negatives cannot. Clearer than any other short treatment.

0:00 / --:--

Concrete scenario

An LSM engine wants to avoid disk reads for keys absent from each immutable table.

Mechanics

A Bloom filter sets k hash-selected bits in an m-bit array. If any queried bit is unset, a correctly constructed and current append-only filter proves the key was not inserted; if all are set, the answer may be a false positive. Size m and k from expected items and target false-positive rate.

Failure mode

Deleting bits from a standard filter can create false negatives. An undersized or stale filter raises error rates, so mutable sets need rebuilds, counting filters, or another design.

Trade-off

Bloom filters save memory and negative lookup I/O but cannot return values or prove presence. Lower false-positive rates consume more memory and hash work.

Worked example: sizing a filter

For n items and a target false-positive rate p:

  • Bits needed: m = -n x ln(p) / (ln 2)^2, about 9.6 x n bits for p = 1%.
  • Best number of hash functions: k = (m / n) x ln 2, about 7 for p = 1%.
Target FP rateBits per itemHash functions kMemory for 100M items
10%4.83about 60 MB
1%9.67about 120 MB
0.1%14.410about 180 MB
0.01%19.213about 240 MB

Each 10x reduction in false positives costs only about 4.8 more bits per item. Compare that with storing the 100M keys themselves (for example 16-byte IDs, about 1.6 GB).

Worked example: LSM read path

A RocksDB/Cassandra key lookup may need to check 10 SSTables. Without filters, a miss costs up to 10 disk reads. With a 1% filter per SSTable, the expected wasted reads for a missing key are 10 x 0.01 = 0.1, a 100x reduction. RocksDB's default configuration uses about 10 bits per key, and Cassandra's bloom_filter_fp_chance defaults to 0.01 (0.1 for LeveledCompaction).

flowchart TD Q["get key"] --> M["Memtable"] M -->|"miss"| F1{"SSTable 1 Bloom filter"} F1 -->|"definitely not"| F2{"SSTable 2 Bloom filter"} F1 -->|"maybe"| R1["Read SSTable 1 index and block"] F2 -->|"definitely not"| F3["... next SSTable"] F2 -->|"maybe"| R2["Read SSTable 2 index and block"]

Variants

VariantAddsCost
Counting Bloom filterDeletes (4-bit counters)About 4x memory
Cuckoo filterDeletes, better space below about 3% FPInserts can fail when nearly full
Scalable Bloom filterGrowth without knowing n up frontChain of filters, slightly slower lookups
Blocked Bloom filterOne cache line per lookup (fast)Slightly higher FP rate at the same size
Xor / ribbon filtersAbout 20-30% less space, static setsMust be built all at once

Real uses: LSM engines (RocksDB, Cassandra, HBase, LevelDB), Chrome's old Safe Browsing malicious-URL prefilter, Akamai's "one-hit-wonder" cache admission (only cache on the second request), and Medium's "already recommended" checks.

Check yourself Question 1 of 3
Reading a Bloom answer recall
From: Mechanics

A lookup checks a key against a correctly built, current Bloom filter and finds all k bits set. What can you conclude?

Bloom Filter (m=16, k=3)
Elements (n): 0 | False Pos Prob: 0.00%

Key Takeaways

A current append-only Bloom filter can prove absence but only probable presence.
Deletion requires rebuilding, counting filters, or another design.
False-positive rate trades memory and hash work against avoided I/O.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue