vault backup: 2026-05-25 23:50:33

This commit is contained in:
hhs
2026-05-25 23:50:33 +08:00
parent 268ee58de6
commit 1e8dbf06b8
5 changed files with 304 additions and 86 deletions
+47 -3
View File
@@ -42,7 +42,25 @@ graph LR
### 方案 A:RedisBloom 模块(服务端)
RedisBloom 是 Redis 官方模块,通过 `BF.ADD` / `BF.EXISTS` 命令直接操作。
RedisBloom 是 Redis 官方模块,**默认不随 Redis 一起安装**,需要单独编译或通过 Docker 加载:
```bash
# Docker 方式(推荐)
docker run -p 6379:6379 redis/redis-stack-server # 内置 RedisBloom
```
核心命令:
| 命令 | 说明 |
|------|------|
| `BF.RESERVE key error_rate capacity` | 创建自定义布隆过滤器(生产环境推荐) |
| `BF.ADD key item` | 添加元素(自动创建默认参数的过滤器) |
| `BF.EXISTS key item` | 查询元素,返回 1=可能存在 / 0=一定不存在 |
| `BF.MADD key item1 item2 ...` | 批量添加 |
| `BF.MEXISTS key item1 item2 ...` | 批量查询 |
> [!tip] 生产环境务必用 `BF.RESERVE` 预创建
> `BF.ADD` 首次调用时会以默认参数自动创建过滤器(容量约 100,误判率 0.01),数据量稍大就会急剧膨胀误判率。正确做法是先 `BF.RESERVE user_filter 0.0001 1000000` 显式声明参数。
```go
// go-redis 调用 RedisBloom
@@ -138,7 +156,9 @@ func (s *BloomCacheService) GetUser(ctx context.Context, userID string) (*User,
return nil, nil // 缓存的空值
}
var user User
json.Unmarshal(data, &user)
if err := json.Unmarshal(data, &user); err != nil {
return nil, err // JSON 反序列化失败
}
return &user, nil
}
@@ -220,7 +240,24 @@ func calcBloomParams(n int, p float64) (m int, k int) {
### 6.1 不支持删除
标准布隆过滤器不支持删除——将某位从 1 置为 0 可能影响其他元素判断。解决方案是**计数布隆过滤器(Counting Bloom Filter)**,用计数器替代 1 bit,删除时计数器减 1。RedisBloom 的 Cuckoo Filter(`CF.ADD` / `CF.EXISTS`)也原生支持删除。
标准布隆过滤器不支持删除——将某位从 1 置为 0 可能影响其他元素判断。两种解决方案:
| 方案 | 原理 | 优点 | 缺点 |
|------|------|------|------|
| **计数布隆过滤器** | 用计数器替代 1 bit,删除时减 1 | 兼容标准布隆过滤器思路 | 内存开销增大 3-4 倍 |
| **Cuckoo Filter** | 基于 cuckoo hashing,直接存储元素指纹 | 支持删除、查询更快、空间更优 | 容量接近满时插入可能失败 |
RedisBloom 中 Cuckoo Filter 的命令:
```bash
CF.RESERVE key capacity # 创建
CF.ADD key item # 添加
CF.EXISTS key item # 查询
CF.DEL key item # 删除(布隆过滤器做不到)
```
> [!tip] 何时用 Cuckoo Filter?
> 如果你的场景**需要删除过期元素**(比如用户注销后移除 ID),优先用 Cuckoo Filter。如果只做"只增不查删"的穿透防护,标准布隆过滤器更简单高效。
### 6.2 需要预加载
@@ -229,6 +266,13 @@ func calcBloomParams(n int, p float64) (m int, k int) {
> [!question] 如果新增了数据但忘记更新布隆过滤器会怎样?
> 新增的 key 在布隆过滤器中查询会返回"不存在",导致合法请求被误拦截。这比缓存穿透更严重——用户明明有数据却查不到。所以务必确保新增数据时同步 `BF.ADD` 或 `filter.AddString()`。
> [!warning] Go-side 过滤器的重启问题
> Go 内存中的布隆过滤器在进程重启后**完全丢失**。两种应对策略:
> 1. **启动时重建**:从 DB 全量加载 ID 重新构建(数据量大时启动慢,可用 goroutine 并行加载)
> 2. **持久化 bit 数组**:将 bit 数组序列化到 Redis 或文件,启动时反序列化恢复(快但需要额外维护一致性)
>
> 如果重建成本不可接受,优先考虑 RedisBloom——数据随 Redis 持久化,应用重启无感知。
### 6.3 误判率随使用上升
实际元素数量远超预估值 `n` 时,误判率会显著上升。建议预留 20%-30% 余量,监控实际元素数量,必要时重建过滤器。