当前位置: 首页 > news >正文

从零手写Java版本的LSM Tree (五):布隆过滤器

🔥 推荐一个高质量的Java LSM Tree开源项目!
https://github.com/brianxiadong/java-lsm-tree
java-lsm-tree 是一个从零实现的Log-Structured Merge Tree,专为高并发写入场景设计。
核心亮点:
⚡ 极致性能:写入速度超过40万ops/秒,完爆传统B+树
🏗️ 完整架构:MemTable跳表 + SSTable + WAL + 布隆过滤器 + 多级压缩
📚 深度教程:12章详细教程,从基础概念到生产优化,每行代码都有注释
🔒 并发安全:读写锁机制,支持高并发场景
💾 数据可靠:WAL写前日志确保崩溃恢复,零数据丢失
适合谁?

  • 想深入理解LSM Tree原理的开发者
  • 需要高写入性能存储引擎的项目
  • 准备数据库/存储系统面试的同学
  • 对分布式存储感兴趣的工程师
    ⭐ 给个Star支持开源!

第5章:布隆过滤器

什么是布隆过滤器?

布隆过滤器 (Bloom Filter) 是一种概率型数据结构,用于快速判断一个元素是否可能存在于集合中。它具有以下特性:

  • 无假阴性: 如果布隆过滤器说元素不存在,那么元素一定不存在
  • 有假阳性: 如果布隆过滤器说元素存在,元素可能不存在
  • 空间高效: 相比哈希表,内存占用极小
  • 时间高效: 查询和插入都是O(k),k是哈希函数数量

布隆过滤器在LSM Tree中的作用

在LSM Tree中,布隆过滤器被广泛应用于SSTable文件中:

查询流程:
1. 检查MemTable (可能存在)
2. 检查Immutable MemTable (可能存在)  
3. 对每个SSTable:a. 检查布隆过滤器 (可能存在?)b. 如果可能存在,读取文件查找c. 如果不存在,直接跳过 (避免磁盘I/O)

Immutable MemTable指的是数据已满,准备写到磁盘中的MemTable

性能提升: 布隆过滤器可以减少90%以上的无效磁盘I/O操作!

核心原理

工作机制图解

步骤1: 初始状态

位置0123456789101112131415
位值0000000000000000

步骤2: 添加 “apple”

  • hash1(“apple”) = 3, hash2(“apple”) = 7, hash3(“apple”) = 12
位置0123456789101112131415
位值0001000100001000

步骤3: 添加 “banana”

  • hash1(“banana”) = 1, hash2(“banana”) = 9, hash3(“banana”) = 15
位置0123456789101112131415
位值0101000101001001

步骤4: 查询测试

元素hash位置位值检查结果说明
“apple”3,7,121,1,1✅ 可能存在确实存在
“cherry”2,6,100,0,0❌ 确定不存在位置2为0
“grape”1,7,91,1,1⚠️ 可能存在假阳性!

核心洞察:

  • 插入: 多个哈希函数将元素映射到不同位置,全部设为1
  • 查询: 检查所有对应位置,任一为0则确定不存在
  • 假阳性: 不同元素可能hash到相同位置,导致误判
  • 无假阴性: 如果元素真实存在,对应位置必然为1

布隆过滤器实现

核心实现

package com.brianxiadong.lsmtree;import java.util.BitSet;/*** 布隆过滤器实现* 用于快速判断键是否可能存在于SSTable中*/
public class BloomFilter {private final BitSet bitSet;               // 位数组,存储布隆过滤器的位private final int size;                    // 位数组的大小private final int hashFunctions;           // 哈希函数的数量// 构造函数:根据预期元素数和假阳性概率初始化public BloomFilter(int expectedElements, double falsePositiveProbability) {// 计算最优位数组大小:m = -n * ln(p) / (ln(2))^2this.size = (int) (-expectedElements * Math.log(falsePositiveProbability)/ (Math.log(2) * Math.log(2)));// 计算最优哈希函数个数:k = (m/n) * ln(2)this.hashFunctions = (int) (size * Math.log(2) / expectedElements);this.bitSet = new BitSet(size);        // 创建位数组}/*** 向布隆过滤器添加元素*/public void add(String key) {for (int i = 0; i < hashFunctions; i++) {      // 使用k个哈希函数int hash = hash(key, i);                   // 计算第i个哈希值bitSet.set(Math.abs(hash % size));         // 设置对应位为1}}/*** 检查元素是否可能存在* 返回false表示绝对不存在* 返回true表示可能存在*/public boolean mightContain(String key) {for (int i = 0; i < hashFunctions; i++) {      // 检查k个哈希位置int hash = hash(key, i);                   // 计算第i个哈希值if (!bitSet.get(Math.abs(hash % size))) {  // 如果任一位为0return false;                          // 确定不存在}}return true;                                   // 所有位都为1,可能存在}/*** 多重哈希函数实现* 使用Double Hashing技术避免实现多个独立的哈希函数*/private int hash(String key, int i) {int hash1 = key.hashCode();                    // 第一个哈希值int hash2 = hash1 >>> 16;                      // 第二个哈希值(高16位)return hash1 + i * hash2;                      // 组合生成第i个哈希值}/*** 获取位数组序列化数据(用于持久化)*/public byte[] toByteArray() {return bitSet.toByteArray();                   // 将BitSet转换为字节数组}/*** 从字节数组恢复布隆过滤器*/public static BloomFilter fromByteArray(byte[] data, int size, int hashFunctions) {BloomFilter filter = new BloomFilter(1000, 0.01); // 临时创建参数filter.bitSet.clear();                         // 清空当前位数组BitSet restored = BitSet.valueOf(data);        // 从字节数组恢复BitSetfilter.bitSet.or(restored);                    // 合并到当前位数组return filter;}
}

代码解析:这个布隆过滤器实现采用了经典的设计模式。构造函数根据数学公式计算最优的位数组大小和哈希函数数量,确保在给定的假阳性率下达到最佳性能。双重哈希技术避免了实现多个独立哈希函数的复杂性,通过组合两个基础哈希值生成所需数量的哈希值。toByteArrayfromByteArray方法支持布隆过滤器的持久化,这在SSTable文件中存储布隆过滤器时非常有用。

小结

布隆过滤器是LSM Tree性能优化的关键组件:

  1. 空间效率: 极小的内存占用
  2. 时间效率: O(k)的查询时间
  3. 假阳性权衡: 接受假阳性换取性能提升
  4. 广泛应用: 缓存、数据库、网络等场景

下一步学习

现在你已经理解了布隆过滤器的工作原理,接下来我们将学习WAL写前日志:


思考题

  1. 为什么布隆过滤器不能支持删除操作?
  2. 如何选择最优的假阳性率?
  3. 在什么情况下布隆过滤器的性能提升不明显?
http://www.xdnf.cn/news/952255.html

相关文章:

  • CppCon 2015 学习:Transducers, from Clojure to C++
  • 蓝桥杯第十届国B 质数拆分
  • 深入解析 ReentrantLock:原理、公平锁与非公平锁的较量
  • DreamO字节开源图像编辑框架
  • IDC智能机房整体解决方案
  • 华为云Flexus+DeepSeek征文|基于华为云一键部署Dify平台,接入DeepSeek大模型,构建数据可视化助手应用实战指南
  • ubuntu22.04 安装docker 和docker-compose
  • windows系统MySQL安装文档
  • 【深度学习新浪潮】什么是credit assignment problem?
  • 编程工具点亮效率之光
  • 九、MySQL执行原理
  • OPenCV CUDA模块光流处理------利用Nvidia GPU的硬件加速能力来计算光流类cv::cuda::NvidiaHWOpticalFlow
  • 【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
  • MAZANOKE结合内网穿透技术实现跨地域图像优化服务的远程访问过程
  • 零基础设计模式——行为型模式 - 命令模式
  • 使用地球观测数据优化云到 GPU 的吞吐量以进行深度学习
  • rm视觉学习1-自瞄部分
  • 使用python进行图像处理—图像标识与NumPy(3)
  • 【PDF识别改名】PDF指定区域OCR识别重命名工具使用教程和注意事项
  • 前缀和题目:寻找数组的中心下标
  • NoSQL 之 Redis 集群
  • JS红宝书笔记 10.6 - 10.10 函数
  • 树莓派超全系列教程文档--(60)树莓派摄像头操作命令及使用其一
  • Cyber Weekly #59
  • 如何在网页里填写 PDF 表格?
  • MyBatis中关于缓存的理解
  • Spring Framework 6:核心升级特性
  • 2023赣州旅游投资集团
  • OptiStruct结构分析与工程应用:传递路径贡献量分析(TPA)
  • 接口 RESTful 中的超媒体:REST 架构的灵魂驱动