---
id: cs-bloom-filter
title: Bloom Filter — 확률적 set 멤버십
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [cs, bloom-filter, probabilistic, vibe-coding]
tech_stack: { language: "TS", applicable_to: ["Backend"] }
applied_in: []
aliases: [bloom filter, cuckoo filter, false positive, BloomD, set membership]
---

# Bloom Filter

> "이 element 가 set 안에 있나?" 를 매우 작은 메모리로 **추정**. False positive O, false negative X. **Cache miss 회피, DB 조회 사전 차단, malicious URL 검사**.

## 📖 핵심 개념
- m-bit array + k hash functions.
- Add: k 개 위치에 1 set.
- Check: k 개 모두 1 → 아마 있음 / 하나라도 0 → 확실 없음.
- False positive rate: m, k, n 으로 결정.

## 💻 코드 패턴

### 단순 구현
```ts
import xxhash from 'xxhash-wasm';

class BloomFilter {
  private bits: Uint8Array;
  
  constructor(private m: number, private k: number, private hash: any) {
    this.bits = new Uint8Array(Math.ceil(m / 8));
  }

  private indices(key: string): number[] {
    const h1 = this.hash.h32(key, 0);
    const h2 = this.hash.h32(key, 0xdeadbeef);
    const r: number[] = [];
    for (let i = 0; i < this.k; i++) {
      r.push((h1 + i * h2) % this.m);
    }
    return r;
  }

  add(key: string) {
    for (const idx of this.indices(key)) {
      this.bits[Math.floor(idx / 8)] |= (1 << (idx % 8));
    }
  }

  has(key: string): boolean {
    for (const idx of this.indices(key)) {
      if ((this.bits[Math.floor(idx / 8)] & (1 << (idx % 8))) === 0) return false;
    }
    return true;
  }
}

// 1M items, 1% FP → m ≈ 9.6M bits, k ≈ 7
const xxh = await xxhash();
const bf = new BloomFilter(9_600_000, 7, xxh);

bf.add('user-42');
bf.has('user-42');     // true
bf.has('user-999');    // false (with 1% FP)
```

### 사이즈 계산
```
n = 예상 items
p = 원하는 false positive (0.01 = 1%)

m = -n * ln(p) / (ln(2)^2)
k = (m/n) * ln(2)
```

```ts
function optimalSize(n: number, p: number) {
  const m = Math.ceil(-n * Math.log(p) / Math.LN2 ** 2);
  const k = Math.round((m / n) * Math.LN2);
  return { m, k };
}

optimalSize(1_000_000, 0.01); // { m: ~9.6M, k: 7 }
optimalSize(1_000_000, 0.001); // { m: ~14.4M, k: 10 }
```

→ 1M items + 1% FP = 1.2 MB.

### Use case 1: Cache pre-check
```ts
// DB query 전에 — 없는 거 확실하면 query 안 함
async function getUser(id: string): Promise<User | null> {
  if (!userBloom.has(id)) return null; // 확실히 없음
  return await db.users.find(id);  // 있을 수 있음 — query
}
```

→ 트래픽의 99% 가 미스면 DB query 99% 줄임.

### Use case 2: Duplicate detection
```ts
// URL crawler: 이미 본 URL 인지
const seen = new BloomFilter(...);

if (!seen.has(url)) {
  await crawl(url);
  seen.add(url);
}
```

→ 1% 정도 미방문 URL 도 skip 가능 — OK.

### Use case 3: Malicious URL (Google Safe Browsing)
```
Local Bloom filter (작은) — 1% FP
빠른 lookup → 의심 → server 에 정확 query
```

### Counting Bloom (delete 가능)
```ts
// bits 대신 4-bit counter
class CountingBloom {
  private counters: Uint8Array;
  // add: increment, remove: decrement
}
```

→ 메모리 4x. Delete 필요할 때.

### Cuckoo Filter (대안)
- Bloom 보다 작은 메모리 + delete 지원.
- False positive lower.
- 구현 복잡.

```ts
import { CuckooFilter } from 'cuckoo-filter';
const cf = new CuckooFilter(1_000_000, 0.001);
cf.add('user-42');
cf.has('user-42');
cf.remove('user-42');
```

### Distributed (Redis BF)
```bash
# Redis Stack 또는 RedisBloom module
BF.RESERVE bf 0.01 1000000  # FP 1%, 1M
BF.ADD bf user-42
BF.EXISTS bf user-42        # 0 or 1
```

```ts
await redis.call('BF.ADD', 'users', 'u42');
const exists = await redis.call('BF.EXISTS', 'users', 'u42');
```

### Scaling Bloom (성장)
```
일반 BF = 고정 size. 초과 → FP 률 증가.
Scaling BF = 가득 차면 새 sub-filter 추가.
```

```bash
BF.RESERVE bf 0.01 1000000 EXPANSION 2
# 자동 확장
```

### Persistence
```bash
# Save bits → disk
fs.writeFileSync('bloom.bin', bf.bits);

# Load
bf.bits = new Uint8Array(fs.readFileSync('bloom.bin'));
```

### Hyperloglog 와 차이
```
Bloom: "이 item 이 있나?" (멤버십)
HLL:   "총 unique 개수" (cardinality)
```

## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| DB pre-filter | Bloom |
| Crawler dedupe | Bloom |
| Delete 필요 | Counting / Cuckoo |
| Distributed | Redis BF |
| Set 정확 | 일반 hash set (메모리 충분) |
| Cardinality | HyperLogLog |

## ❌ 안티패턴
- **FP 률 0 가정**: 확률이라 — fallback 필수.
- **사이즈 못 맞춤 (예상 보다 큼)**: FP 폭증. scaling BF.
- **Hash 1개**: collision 많음. k=7 정도.
- **Delete 시도 일반 BF**: 가능 X.
- **Bloom = security 만 의존**: 1% FP — 정확 검증 필요.
- **Bits 인쇄 / log**: 큰 객체.

## 🤖 LLM 활용 힌트
- DB / cache pre-filter 강력 ROI.
- 사이즈 = optimal formula.
- Redis BF 가 distributed easy.
- Delete = Counting 또는 Cuckoo.

## 🔗 관련 문서
- [[CS_Probabilistic_Data_Structures]]
- [[CS_Consistent_Hashing]]
- [[DB_Redis_Patterns]]
