How to Build a Bloom Filter to Reduce Cache Misses in Python

Implement a probabilistic Bloom filter in Python that lets a cache quickly determine which keys are definitely not present, reducing expensive source lookups on cache misses.

Medium Python 3.9+ Aug 9, 2026 Caching & Redis 14 views 0 copies

Python code

61 lines
Python 3.9+
import hashlib
import random

class BloomFilter:
    def __init__(self, size=100, num_hashes=3):
        self.size = size
        self.num_hashes = num_hashes
        self.bit_array = [0] * size

    def _hashes(self, item):
        result = []
        for i in range(self.num_hashes):
            hash_value = int(hashlib.md5(f"{i}:{item}".encode()).hexdigest(), 16)
            result.append(hash_value % self.size)
        return result

    def add(self, item):
        for index in self._hashes(item):
            self.bit_array[index] = 1

    def contains(self, item):
        return all(self.bit_array[index] == 1 for index in self._hashes(item))


class Cache:
    def __init__(self, capacity=5):
        self.capacity = capacity
        self.store = {}
        self.bloom = BloomFilter()


    def get(self, key):
        if key in self.store:
            print(f"CACHE HIT: {key}")
            return self.store[key]
        
        if self.bloom.contains(key):
            print(f"CACHE MISS + BLOOM POSITIVE: {key} (likely in cache but evicted)")
        else:
            print(f"CACHE MISS + BLOOM NEGATIVE: {key} (definitely not cached)")

        value = self._load_from_source(key)
        self._cache(key, value)
        return value

    def _load_from_source(self, key):
        return f"data_{key}"

    def _cache(self, key, value):
        self.store[key] = value
        self.bloom.add(key)
        if len(self.store) > self.capacity:
            evicted_key = next(iter(self.store))
            del self.store[evicted_key]
            print(f"EVICTED: {evicted_key}")


if __name__ == "__main__":
    cache = Cache(capacity=3)
    for key in ["a", "b", "c", "d", "a", "e"]:
        cache.get(key)

Output

stdout
CACHE MISS + BLOOM NEGATIVE: a (definitely not cached)
CACHE MISS + BLOOM NEGATIVE: b (definitely not cached)
CACHE MISS + BLOOM NEGATIVE: c (definitely not cached)
CACHE MISS + BLOOM NEGATIVE: d (definitely not cached)
EVICTED: a
CACHE HIT: a
EVICTED: b
CACHE MISS + BLOOM POSITIVE: e (likely in cache but evicted)

How it works

The Bloom filter uses hashlib.md5 to generate multiple hash positions for each key, setting bits in a fixed-size array. When checking membership, it returns True only if all bits are set — false positives are possible, but false negatives are impossible. The cache stores a small LRU-like dict and adds keys to the Bloom filter even after eviction, so it can distinguish 'definitely not seen' from 'possibly evicted'. This avoids hitting the source for keys the Bloom filter knows are new, cutting latency on repeated misses. The add operation is O(k) where k is the number of hashes, keeping both insert and lookup cheap for high-throughput caches.

Common mistakes

  • Recreating a Bloom filter per cache entry instead of one shared instance
  • Using a single hash function, which drastically increases false-positive rate
  • Forgetting that Bloom filters can't delete keys — evicted entries stay marked
  • Choosing a too-small bit array, causing collision-heavy false positives

Variations

  1. Use `pybloom_live` or `bloom_filter2` pip packages for production-optimized implementations
  2. Serialize the bit array to Redis with SETBIT/GETBIT for a distributed cache filter

Real-world use cases

  • Reduce database queries in front-end caches by skipping lookups for keys never requested before.
  • Prevent thundering herd problems when many clients request the same missing cache key simultaneously.
  • Filter out known-invalid user IDs or tokens before hitting an auth service or rate limiter.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Caching & Redis

Related tutorials and quizzes for this topic.