HBase HFile:数据是如何存储的
HBase 的数据最终存储在 HFile 中。
理解 HFile 的结构,是理解 HBase 高性能读写的关键。
HFile 是什么?
HFile 是 HBase 的存储格式,基于 Hadoop 的 Sequence File,特点是有序、压缩、可切分。
┌─────────────────────────────────────────────────────────────┐
│ HFile v3 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Block Index (可变大小) │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ Block Index Entry 1: key, offset, size │ │ │
│ │ │ Block Index Entry 2: key, offset, size │ │ │
│ │ │ Block Index Entry N: key, offset, size │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Meta Index Block │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Data Blocks │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │ Block 1 │ │ Block 2 │ │ Block N │ │ │
│ │ │ 64KB │ │ 64KB │ │ 64KB │ │ │
│ │ └───────────┘ └───────────┘ └───────────┘ │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ KeyValue1 | KeyValue2 | KeyValue3 | ... │ │ │
│ │ │ 按 RowKey 排序,同一行可能跨 Block │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Meta Block │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ File Info (可变大小) │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ TotalBytes, NumEntries, LastKey, ... │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Trailer (固定大小) │ │
│ │ ┌───────────────────────────────────────────────┐ │ │
│ │ │ Magic, Version, IndexOffset, ... │ │ │
│ │ └───────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘KeyValue 结构
HFile 中存储的基本单元是 KeyValue:
┌─────────────────────────────────────────────────────────────┐
│ KeyValue 结构 │
│ │
│ ┌───────────┬───────────┬───────────┬───────────┬─────────┐│
│ │ Key Length │ Value Length │ Timestamp │ Type │ Key │ Value │
│ │ (4B) │ (4B) │ (8B) │ (1B) │ ... │ ... │
│ └───────────┴───────────┴───────────┴───────────┴─────────┘│
│ │
│ Key = RowKey Length + RowKey + CF Length + CF + CQ + Timestamp │
└─────────────────────────────────────────────────────────────┘Key 的组成:
- RowKey Length(4 字节)
- RowKey(N 字节)
- Column Family Length(1 字节)
- Column Family(N 字节)
- Column Qualifier(N 字节)
- Timestamp(8 字节)
- Key Type(1 字节):Put / Delete / ...
Block 结构
Data Block
数据块是 HFile 存储数据的基本单元:
java
// HFile Block 配置
public class HFileConfig {
// 块大小(默认 64KB)
// 增大块:适合顺序读
// 减小块:适合随机读
public static final int BLOCK_SIZE = 64 * 1024; // 64KB
}Block Index
Block Index 存储每个 Block 的起始 Key 和位置:
Block Index 查找过程:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 加载 Block Index 到内存 │
│ 2. 二分查找:定位目标 Key 所在的 Block Index Entry │
│ 3. 读取对应 Block 的数据 │
│ │
│ 查找复杂度:O(log N) + O(1) │
│ N = Block 数量 │
│ │
└─────────────────────────────────────────────────────────────┘Bloom Filter
Bloom Filter 用于快速判断某个 Key 是否存在:
┌─────────────────────────────────────────────────────────────┐
│ Bloom Filter 工作原理 │
│ │
│ Key1 ──┐ │
│ Key2 ──┼──→ Hash Functions ──→ Bit Array ──→ 存在? │
│ Key3 ──┘ (多个哈希) (位数组) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 0 1 1 0 1 0 0 1 0 1 1 0 0 1 0 0 ... │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 特点: │
│ - 存在不一定准确(可能有假阳性) │
│ - 不存在一定准确(没有假阴性) │
│ - 空间效率高(每个 Key 只占几位) │
│ │
└─────────────────────────────────────────────────────────────┘在 HFile 中的应用
java
// 创建带 Bloom Filter 的 Column Family
ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder
.of("info")
.setBloomFilterType(BloomType.ROW) // ROW: 按 RowKey 过滤
// ROWCOL: 按 RowKey + Column 过滤
.build();压缩
HFile 支持多种压缩算法:
| 压缩算法 | 压缩率 | CPU 开销 | 适用场景 |
|---|---|---|---|
| NONE | 无 | 无 | |
| SNAPPY | 中 | 低 | 平衡之选 |
| LZO | 中 | 中 | 通用 |
| GZ | 高 | 高 | 冷数据 |
| ZSTD | 高 | 中 | 推荐使用 |
java
// 配置压缩
ColumnFamilyDescriptor cfd = ColumnFamilyDescriptorBuilder
.of("info")
.setCompressionType(Compression.Algorithm.SNAPPY)
.build();读取流程
HFile 读取流程:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 加载 Trailer,获取 Index Block 位置 │
│ │
│ 2. 加载 Index Block,建立 Block Index │
│ │
│ 3. 二分查找 Index Block,定位目标 Key 在哪个 Data Block │
│ │
│ 4. 读取目标 Data Block │
│ │
│ 5. 在 Data Block 内顺序查找目标 Key │
│ │
│ 6. 如果开启 Bloom Filter,步骤 3 可以跳过不存在的 Key │
│ │
└─────────────────────────────────────────────────────────────┘面试追问方向
- HFile 的数据是按什么顺序存储的?
- Bloom Filter 有什么优缺点?
下一节,我们来了解 HBase 的写入流程。
