6.2 布隆过滤器与读缓存:读路径的两台加速器 本节摘要:读路径(3.3 节)的两大减免项在这里展开:布隆过滤器用少量内存把"一定不在本文件"的判断提前,布隆块直接随 HFile 落盘;BlockCache 分堆内 L1 与堆外 L2 两层,缓存命中把磁盘 IO 变成内存拷贝。本节讲两者的原理、配置与命中率调优。 布隆过滤器:可能说谎的"没有" 先解决一个 3.3 节遗留的问题:Store 里有 8 个 HFile,一次点查难道每个都要查?大部分文件里根本没有这个行键,能不能先廉价地问一句"你这里有它吗"? 布隆过滤器就是这个廉价问答机:一个位数组加 k 个哈希函数。写入时,把行键经 k 个哈希映射到位数组上置 1;查询时同样哈希一遍,若任一位为 0,则该键一定没写过;
本节摘要:读路径(3.3 节)的两大减免项在这里展开:布隆过滤器用少量内存把"一定不在本文件"的判断提前,布隆块直接随 HFile 落盘;BlockCache 分堆内 L1 与堆外 L2 两层,缓存命中把磁盘 IO 变成内存拷贝。本节讲两者的原理、配置与命中率调优。
先解决一个 3.3 节遗留的问题:Store 里有 8 个 HFile,一次点查难道每个都要查?大部分文件里根本没有这个行键,能不能先廉价地问一句"你这里有它吗"?
布隆过滤器就是这个廉价问答机:一个位数组加 k 个哈希函数。写入时,把行键经 k 个哈希映射到位数组上置 1;查询时同样哈希一遍,若任一位为 0,则该键一定没写过;若全为 1,则"可能存在",去查(可能是哈希碰撞的误判)。核心性质一句话:不漏报、只误报——说"没有"就真的没有,说"有"可能看走眼,多付一次查找而已,不会返回错数据。
HBase 的布隆按数据块分段存放在 HFile 内部(2.3 节的布隆块),随文件一起生成、常驻可缓存,代价是写入时多算哈希、文件多占约 1% 空间。对随机点查场景,它能把读路径从"8 个文件全查"砍到"1–2 个候选",配合布隆块本身可被 BlockCache 缓存,判定几乎零 IO——第 6 章 _index 的漏斗图就是这个过程。
ROW 还是 ROWCOL? 布隆键可以是行(ROW,默认)或行加列(ROWCOL)。按行键前缀点查的表 ROW 够用;若查询模式是"同一行的不同列散布在大量 Cell"(稀疏随机列访问),ROWCOL 过滤更精准,但布隆空间翻倍。5.2 节建表参数里冷列族设 NONE 省内存,就是这笔账的另一面。
hbase:120:0> alter 'orders', {NAME => 'cf', BLOOMFILTER => 'ROWCOL'}
验证布隆收益的方法:Web UI 的 Region 指标里有布隆相关计数(被布隆过滤掉的块请求数),压测对比开关前后的读延迟即可量化。
3.3 节说过 BlockCache 占 RegionServer 堆的约 40%,按 64 KB 数据块为缓存粒度。2.x 的默认实现是两层结构:
L1——堆内 LruBlockCache。 按 LRU(分年轻代/老年代多优先级)淘汰,命中速度最快(内存直读),但 Java 堆内对象开销大(块数据 64 KB 变成 byte 数组加对象头,实际占空比高),且加剧 GC 压力——大缓存长 GC 是读延迟毛刺的经典来源(第 7 章排查表里的常客)。
L2——堆外 BucketCache。 用 DirectByteBuffer 或内存映射文件分配的一大块堆外内存,切成 4 KB 到 512 KB 的桶(bucket),块数据按大小入桶。免 GC、内存利用率接近物理内存真实值,容量可以开得远大于堆内(几十 GB 常见)。命中比 L1 慢一次堆外拷贝,但仍比磁盘 IO 快两个量级。
默认策略是 L1 与 L2 组成的分层缓存:数据块优先在 L1 尝试,未命中查 L2,都未命中读 HDFS;读到的块按类型入不同层(索引与布隆块倾向 L1 常驻,数据块倾向大容量 L2)。典型配置(RegionServer 级):
hfile.block.cache.size = 0.4 # 缓存总占比 hbase.bucketcache.ioengine = offheap # 堆外引擎 hbase.bucketcache.size = 32768 # L2 大小 MB Reserve: 堆内 L1 自动为总配置减去 L2 的部分
命中率是第一观察指标。Web UI 的 BlockCache 命中率(Block Cache hit ratio)健康线大致:点查为主的画像/订单表应在 85% 以上,扫描为主的时序表天然偏低(扫过不再扫,可以配 setCacheBlocks(false) 别污染缓存,4.3 节)。命中率低的排查顺序:容量是否不足、扫描流量是否冲刷缓存、块的局部性是否被行键设计打散。
⚠️ 常见坑:把 BucketCache 开在 file 模式(SSD 上)却在机械盘集群照抄配置,"缓存"比数据盘还慢。ioengine 的选择要与硬件实况一致:有充足内存选 offheap,SSD 集群可选 file。
把两者放回 3.3 节的读路径:布隆的位图块与 HFile 索引块本身也被 BlockCache 缓存——也就是说布隆判定在稳态下是纯内存操作。于是热数据的点查路径完整形态是:Meta 缓存寻址(1.3)→ 布隆内存判定排除 7/8 文件 → L1 命中数据块 → 零磁盘 IO 返回。读延迟从毫秒级进入百微秒级,这就是 HBase 点查的完全体。
冷数据路径则老实得多:布隆筛出候选 → L1/L2 都未命中 → HDFS 读块(可能还跨 DataNode)→ 回填缓存。冷热差距可达百倍,容量规划时"缓存能兜住多少工作集"直接决定 P99。
💡 关键直觉:布隆解决"别查没数据的文件",缓存解决"别读读过的块",一个作用于文件维度、一个作用于块维度,互不替代、可叠加。读优化预算有限时先看命中率再谈布隆——命中率低说明工作集都没装下,布隆只是止血。
读写两侧的武器都齐了。6.3 节把 HBase 放回大数据生态:接流、批算、检索各由谁干。