SCALABILITY & PERFORMANCE / SYSTEM CONCEPT BRIEF

Probabilistic data structures

Probabilistic data structures answer questions about huge data sets using tiny, fixed memory by accepting a small, controlled error.

AdvancedPhase 03 / Topic 13 of 13RequirementsTrade-offsFailure modes
01

Overview

Probabilistic data structures answer questions about huge data sets using tiny, fixed memory by accepting a small, controlled error. A Bloom filter tests set membership ('definitely not present' or 'probably present'). HyperLogLog estimates the number of distinct items (unique visitors) within about 1% using kilobytes. Count-Min Sketch estimates how often each item appears (heavy hitters).

They matter at scale. Tracking 1 billion unique user IDs exactly needs gigabytes; a HyperLogLog needs about 12 KB. Databases, caches, CDNs, and analytics systems use these structures to skip unnecessary disk reads, count uniques, and find trending items in streams.

A bouncer's guest list summary

Instead of carrying a thousand-page guest list, the bouncer has a small card of marks. If your marks are not all on the card, you are definitely not invited. If they are, you are probably invited, so check the full list just to be sure.

02

When to use it

  • Avoiding expensive lookups for items that definitely do not exist.
  • Counting unique visitors, searches, or devices at massive scale.
  • Finding top-K or trending items in a stream.
  • Situations where approximate answers are acceptable and memory is tight.
03

Where it shows up in interviews

Avoid expensive misses

Recognize it when: most lookups are for items that do not exist.

  • Design a web crawler (seen URLs)
  • Design a username availability check
  • Prevent cache penetration
Approximate analytics

Recognize it when: unique counts or frequencies over billions of events.

  • Design YouTube view counting
  • Design trending hashtags
  • Design an ad click aggregator
04

Where it is used in real software

Cassandra, HBase, RocksDB

Use Bloom filters per SSTable to skip files that cannot contain a key, saving disk reads.

Redis

Provides HyperLogLog (PFADD, PFCOUNT) for unique counts in 12 KB per key, plus Bloom and Count-Min modules.

Analytics engines

BigQuery, Druid, and Presto use HyperLogLog for fast APPROX_COUNT_DISTINCT queries.

05

Key terms

False positive
The structure says 'maybe present' when the item is absent (Bloom filter).
False negative
Saying 'absent' when present; Bloom filters never do this.
Bloom filter
Bit array plus k hash functions for membership tests.
HyperLogLog
Estimates cardinality from the maximum run of leading zeros in hashed values.
Count-Min Sketch
Grid of counters giving upper-bound frequency estimates.
06

How it works, step by step

  1. 1
    Choose the question

    Membership (Bloom), distinct count (HyperLogLog), or frequency (Count-Min Sketch).

  2. 2
    Choose acceptable error

    For example 1% false positives, which determines memory and hash count.

  3. 3
    Hash items

    Use fast, well-distributed hash functions (MurmurHash, xxHash).

  4. 4
    Update on each event

    Set bits, update registers, or increment counters in O(1).

  5. 5
    Query and verify when needed

    Treat 'maybe' as a hint and confirm with the source of truth if exactness matters.

Bloom filter with 10 bits and 3 hash functions
Step 1 / 5
0
0
0
1
0
2
0
3
0
4
0
5
0
6
0
7
0
8
0
9

STEP 1Empty filter, all bits are 0.

07

Exact vs probabilistic

1 billion distinct user IDs (16 bytes each)

Step 1 / 3
QuestionExact structureMemoryProbabilisticMemoryError
Seen this ID?Hash set~16+ GBBloom filter (1% FP)~1.2 GB1% false positives
How many unique?Hash set~16+ GBHyperLogLog~12 KB~0.8% standard error
How often was X seen?Hash map of counts~24+ GBCount-Min Sketchfew MBOverestimates only

NOWQuestion: Seen this ID? | Exact structure: Hash set | Memory: ~16+ GB | Probabilistic: Bloom filter (1% FP) | Memory: ~1.2 GB | Error: 1% false positives

Orders-of-magnitude memory savings in exchange for small, bounded, one-sided errors.

08

Implementation

// Bloom filter with double hashingclass BloomFilter {  private bits: Uint8Array;  constructor(private size: number, private hashes: number) { this.bits = new Uint8Array(Math.ceil(size / 8)); }   private hash(value: string, seed: number) {    let h = 2166136261 ^ seed;                      // FNV-1a variant    for (let i = 0; i < value.length; i++) { h ^= value.charCodeAt(i); h = Math.imul(h, 16777619); }    return h >>> 0;  }  private positions(value: string) {    const h1 = this.hash(value, 0), h2 = this.hash(value, 0x9747b28c) || 1;    return Array.from({ length: this.hashes }, (_, i) => (h1 + i * h2) % this.size);  }  add(value: string) { for (const p of this.positions(value)) this.bits[p >> 3] |= 1 << (p & 7); }  mightContain(value: string) { return this.positions(value).every((p) => (this.bits[p >> 3] & (1 << (p & 7))) !== 0); }   // Optimal sizing for n items and false-positive rate p  static forCapacity(n: number, p: number) {    const m = Math.ceil((-n * Math.log(p)) / Math.LN2 ** 2);    return new BloomFilter(m, Math.max(1, Math.round((m / n) * Math.LN2)));  }} const seen = BloomFilter.forCapacity(1_000_000, 0.01);   // ~1.2 MBseen.add("https://example.com/a");seen.mightContain("https://example.com/a"); // trueseen.mightContain("https://example.com/z"); // false (almost always)
09

Complexity and performance

Add / queryO(k) hashes

Constant time regardless of data size.

Bloom filter memory~9.6 bits per item at 1% FP

Independent of item size.

HyperLogLog memory~12 KB

For billions of distinct items at ~0.8% error.

10

Trade-offs

Accuracy vs memory

Lower error rates need more memory and hash functions; choose error bounds from business needs.

No deletions or listing

Standard Bloom filters cannot delete items or list members; use counting Bloom filters or cuckoo filters if deletion is required.

11

Variants and related techniques

Cuckoo filter

Membership test that supports deletion with similar space efficiency.

Top-K with Count-Min + heap

Maintain a small heap of candidates to find trending items in streams.

12

Common mistakes

  • Treating 'maybe present' as certain.

    Fix: Confirm with the source of truth when correctness matters.

  • Undersized Bloom filters.

    Fix: False positives explode as the filter fills; size for expected capacity and rebuild as it grows.

13

Interview questions

How would you avoid hitting the database for usernames that do not exist?

Keep a Bloom filter of existing usernames. If it says the name is absent, it is definitely available and needs no database check; if it says maybe present, confirm with the database. False positives only cost an extra lookup.

How do you count unique daily visitors for billions of events?

Use a HyperLogLog per day (for example Redis PFADD/PFCOUNT). It estimates cardinality with about 1% error in 12 KB and can be merged across days or servers.

14

Practice problems

ProblemDifficultyWhat it trains
Implement a Bloom filter and measure its false-positive rateMediumSizing and hashing.
Design trending hashtags with Count-Min SketchHardStreaming top-K.