HBase读写流程 #

一、写入流程概述 #

HBase的写入流程经过多个组件协作,确保数据安全和高性能。

1.1 写入流程图 #

text
┌─────────────────────────────────────────────────────────────────────┐
│                         HBase 写入流程                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────┐                                                        │
│  │  Client │                                                        │
│  └────┬────┘                                                        │
│       │                                                              │
│       │ 1. 定位Region                                                │
│       ▼                                                              │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  ZooKeeper → hbase:meta → RegionServer                      │   │
│  └─────────────────────────────────────────────────────────────┘   │
│       │                                                              │
│       │ 2. 发送写请求                                                │
│       ▼                                                              │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                     RegionServer                             │   │
│  │  ┌─────────────────────────────────────────────────────┐    │   │
│  │  │  3. 写WAL (Write-Ahead Log)                         │    │   │
│  │  │     └── 顺序写入HDFS,保证持久性                     │    │   │
│  │  └─────────────────────────────────────────────────────┘    │   │
│  │                          │                                   │   │
│  │                          ▼                                   │   │
│  │  ┌─────────────────────────────────────────────────────┐    │   │
│  │  │  4. 写MemStore                                      │    │   │
│  │  │     └── 写入内存,有序存储                           │    │   │
│  │  └─────────────────────────────────────────────────────┘    │   │
│  │                          │                                   │   │
│  │                          ▼                                   │   │
│  │  ┌─────────────────────────────────────────────────────┐    │   │
│  │  │  5. 返回ACK                                         │    │   │
│  │  │     └── 确认写入成功                                 │    │   │
│  │  └─────────────────────────────────────────────────────┘    │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

1.2 写入步骤详解 #

text
写入步骤
├── 1. Client定位Region
│   ├── 查询ZooKeeper获取meta表位置
│   ├── 查询meta表获取目标Region
│   └── 缓存Region位置信息
│
├── 2. 发送写请求
│   └── 直接向RegionServer发送Put请求
│
├── 3. 写WAL
│   ├── 顺序写入HDFS
│   ├── 确保数据持久化
│   └── 支持同步/异步模式
│
├── 4. 写MemStore
│   ├── 写入内存SkipList
│   ├── 数据有序存储
│   └── 触发Flush条件检查
│
└── 5. 返回确认
    └── 向Client返回写入成功

二、写入详细流程 #

2.1 Region定位 #

text
┌─────────────────────────────────────────────────────────────────────┐
│                         Region 定位                                  │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. 首次访问                                                        │
│     ┌───────────────────────────────────────────────────────────┐  │
│     │  Client → ZooKeeper                                       │  │
│     │         获取meta表所在RegionServer                        │  │
│     └───────────────────────────────────────────────────────────┘  │
│                          │                                          │
│                          ▼                                          │
│     ┌───────────────────────────────────────────────────────────┐  │
│     │  Client → meta表RegionServer                              │  │
│     │         根据RowKey查询目标Region                          │  │
│     └───────────────────────────────────────────────────────────┘  │
│                          │                                          │
│                          ▼                                          │
│     ┌───────────────────────────────────────────────────────────┐  │
│     │  Client 缓存Region位置                                     │  │
│     │         后续访问直接使用缓存                               │  │
│     └───────────────────────────────────────────────────────────┘  │
│                                                                     │
│  2. 后续访问                                                        │
│     └── 直接使用缓存的Region位置                                    │
│                                                                     │
│  3. 缓存失效                                                        │
│     ├── Region移动时失效                                            │
│     └── 访问失败时刷新                                              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2.2 WAL写入 #

text
┌─────────────────────────────────────────────────────────────────────┐
│                         WAL 写入                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  WAL写入流程:                                                      │
│                                                                     │
│  1. 序列化KeyValue                                                 │
│     └── 将Put操作序列化为WALEdit                                    │
│                                                                     │
│  2. 获取WAL序号                                                    │
│     └── MVCC保证顺序性                                              │
│                                                                     │
│  3. 写入WAL文件                                                    │
│     ┌───────────────────────────────────────────────────────────┐  │
│     │  WAL文件结构                                               │  │
│     │  ┌─────────────────────────────────────────────────────┐  │  │
│     │  │  WAL Header                                         │  │  │
│     │  │  - 文件格式版本                                      │  │  │
│     │  │  - 加密信息                                          │  │  │
│     │  └─────────────────────────────────────────────────────┘  │  │
│     │  ┌─────────────────────────────────────────────────────┐  │  │
│     │  │  WAL Entry 1                                        │  │  │
│     │  │  - Region信息                                        │  │  │
│     │  │  - WALEdit (KeyValue列表)                            │  │  │
│     │  │  - 时间戳                                            │  │  │
│     │  └─────────────────────────────────────────────────────┘  │  │
│     │  ┌─────────────────────────────────────────────────────┐  │  │
│     │  │  WAL Entry 2                                        │  │  │
│     │  └─────────────────────────────────────────────────────┘  │  │
│     └───────────────────────────────────────────────────────────┘  │
│                                                                     │
│  4. 同步到磁盘                                                      │
│     └── 根据WAL类型决定是否等待                                     │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2.3 MemStore写入 #

text
┌─────────────────────────────────────────────────────────────────────┐
│                         MemStore 写入                                │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  MemStore写入流程:                                                 │
│                                                                     │
│  1. 获取行锁                                                        │
│     └── 保证同一行的原子性                                          │
│                                                                     │
│  2. 更新MVCC                                                       │
│     └── 分配写序号                                                  │
│                                                                     │
│  3. 写入Cell                                                       │
│     ┌───────────────────────────────────────────────────────────┐  │
│     │  MemStore (SkipList)                                       │  │
│     │  ┌─────────────────────────────────────────────────────┐  │  │
│     │  │  row3 │ cf:col → value3 (ts=1704067202)             │  │  │
│     │  ├─────────────────────────────────────────────────────┤  │  │
│     │  │  row2 │ cf:col → value2 (ts=1704067201)             │  │  │
│     │  ├─────────────────────────────────────────────────────┤  │  │
│     │  │  row1 │ cf:col → value1 (ts=1704067200)             │  │  │
│     │  └─────────────────────────────────────────────────────┘  │  │
│     │  排序规则:RowKey → CF → CQ → Timestamp(倒序)             │  │
│     └───────────────────────────────────────────────────────────┘  │
│                                                                     │
│  4. 释放行锁                                                        │
│                                                                     │
│  5. 检查Flush条件                                                  │
│     ├── MemStore大小达到阈值                                        │
│     └── RegionServer全局内存压力                                    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2.4 Flush触发 #

text
Flush触发条件
├── MemStore级别
│   └── 单个MemStore达到128MB
│
├── Region级别
│   └── Region所有MemStore总和达到阈值
│
├── RegionServer级别
│   ├── 全局MemStore达到高水位(阻塞写入)
│   └── 全局MemStore达到低水位(强制Flush)
│
├── WAL级别
│   └── WAL文件数量过多
│
└── 手动触发
    └── flush命令

三、读取流程概述 #

3.1 读取流程图 #

text
┌─────────────────────────────────────────────────────────────────────┐
│                         HBase 读取流程                               │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────┐                                                        │
│  │  Client │                                                        │
│  └────┬────┘                                                        │
│       │                                                              │
│       │ 1. 定位Region                                                │
│       ▼                                                              │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  ZooKeeper → hbase:meta → RegionServer                      │   │
│  └─────────────────────────────────────────────────────────────┘   │
│       │                                                              │
│       │ 2. 发送读请求                                                │
│       ▼                                                              │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                     RegionServer                             │   │
│  │                                                              │   │
│  │  ┌─────────────────────────────────────────────────────┐    │   │
│  │  │  3. 查询BlockCache                                   │    │   │
│  │  │     └── 缓存命中直接返回                             │    │   │
│  │  └─────────────────────────────────────────────────────┘    │   │
│  │                          │                                   │   │
│  │                          ▼                                   │   │
│  │  ┌─────────────────────────────────────────────────────┐    │   │
│  │  │  4. 查询MemStore                                     │    │   │
│  │  │     └── 查找最新数据                                 │    │   │
│  │  └─────────────────────────────────────────────────────┘    │   │
│  │                          │                                   │   │
│  │                          ▼                                   │   │
│  │  ┌─────────────────────────────────────────────────────┐    │   │
│  │  │  5. 查询HFile                                        │    │   │
│  │  │     ├── Bloom Filter过滤                            │    │   │
│  │  │     ├── Block Index定位                             │    │   │
│  │  │     └── 读取Data Block                              │    │   │
│  │  └─────────────────────────────────────────────────────┘    │   │
│  │                          │                                   │   │
│  │                          ▼                                   │   │
│  │  ┌─────────────────────────────────────────────────────┐    │   │
│  │  │  6. 合并结果                                         │    │   │
│  │  │     └── 合并多版本,返回最新数据                     │    │   │
│  │  └─────────────────────────────────────────────────────┘    │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

3.2 读取步骤详解 #

text
读取步骤
├── 1. Client定位Region
│   └── 同写入流程
│
├── 2. 发送读请求
│   └── 向RegionServer发送Get/Scan请求
│
├── 3. 查询BlockCache
│   ├── 检查缓存是否命中
│   └── 命中则直接返回
│
├── 4. 查询MemStore
│   ├── 查找最新写入数据
│   └── 数据按RowKey排序
│
├── 5. 查询HFile
│   ├── Bloom Filter快速过滤
│   ├── Block Index定位数据块
│   └── 读取Data Block
│
└── 6. 合并结果
    ├── 合并多个数据源
    └── 返回最新版本数据

四、读取详细流程 #

4.1 BlockCache查询 #

text
┌─────────────────────────────────────────────────────────────────────┐
│                         BlockCache 查询                              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  BlockCache结构:                                                   │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  LruBlockCache                                                 │ │
│  │  ┌─────────────────────────────────────────────────────────┐  │ │
│  │  │  Single Access (25%)                                    │  │ │
│  │  │  - 单次访问的数据块                                      │  │ │
│  │  └─────────────────────────────────────────────────────────┘  │ │
│  │  ┌─────────────────────────────────────────────────────────┐  │ │
│  │  │  Multi Access (50%)                                     │  │ │
│  │  │  - 多次访问的数据块                                      │  │ │
│  │  └─────────────────────────────────────────────────────────┘  │ │
│  │  ┌─────────────────────────────────────────────────────────┐  │ │
│  │  │  In-Memory Access (25%)                                 │  │ │
│  │  │  - 常驻内存的数据块                                      │  │ │
│  │  └─────────────────────────────────────────────────────────┘  │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  查询流程:                                                         │
│  1. 计算Block Key                                                  │
│  2. 查找缓存                                                        │
│  3. 命中则更新访问计数                                              │
│  4. 未命中则从HFile读取                                             │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4.2 HFile查询 #

text
┌─────────────────────────────────────────────────────────────────────┐
│                         HFile 查询                                   │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  查询流程:                                                         │
│                                                                     │
│  1. Bloom Filter检查                                               │
│     ┌───────────────────────────────────────────────────────────┐  │
│     │  检查RowKey是否可能存在                                    │  │
│     │  ├── 可能存在 → 继续查询                                   │  │
│     │  └── 不存在 → 跳过该HFile                                  │  │
│     └───────────────────────────────────────────────────────────┘  │
│                                                                     │
│  2. Block Index查找                                                │
│     ┌───────────────────────────────────────────────────────────┐  │
│     │  Root Index                                                │  │
│     │  ├── Data Index Block 1 [startKey1, offset1]              │  │
│     │  ├── Data Index Block 2 [startKey2, offset2]              │  │
│     │  └── Data Index Block 3 [startKey3, offset3]              │  │
│     │                                                             │  │
│     │  二分查找定位目标Block                                      │  │
│     └───────────────────────────────────────────────────────────┘  │
│                                                                     │
│  3. 读取Data Block                                                 │
│     ┌───────────────────────────────────────────────────────────┐  │
│     │  从HDFS读取Block                                           │  │
│     │  ├── 解压缩                                                │  │
│     │  ├── 放入BlockCache                                        │  │
│     │  └── 在Block内二分查找                                     │  │
│     └───────────────────────────────────────────────────────────┘  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4.3 结果合并 #

text
┌─────────────────────────────────────────────────────────────────────┐
│                         结果合并                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  数据来源(按时间戳倒序):                                         │
│                                                                     │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  MemStore     │ ts=1704067203 │ value=v3                      │ │
│  │  StoreFile 1  │ ts=1704067202 │ value=v2                      │ │
│  │  StoreFile 2  │ ts=1704067201 │ value=v1                      │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  合并规则:                                                         │
│  1. 按时间戳倒序排列                                                │
│  2. 返回最新版本                                                    │
│  3. 跳过已删除标记                                                  │
│  4. 应用TTL过滤                                                     │
│                                                                     │
│  返回结果:value=v3 (ts=1704067203)                                │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

五、Scan操作 #

5.1 Scan流程 #

text
┌─────────────────────────────────────────────────────────────────────┐
│                         Scan 流程                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  Scan特点:                                                         │
│  ├── 范围查询                                                       │
│  ├── 可能跨多个Region                                               │
│  └── 使用迭代器模式                                                 │
│                                                                     │
│  Scan流程:                                                         │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  1. 定位起始Region                                            │ │
│  │     └── 根据StartRow定位                                      │ │
│  │                                                               │ │
│  │  2. 创建RegionScanner                                         │ │
│  │     └── 每个Region一个Scanner                                 │ │
│  │                                                               │ │
│  │  3. 创建StoreScanner                                          │ │
│  │     └── 每个Store一个Scanner                                  │ │
│  │                                                               │ │
│  │  4. 创建StoreFileScanner                                      │ │
│  │     └── 每个StoreFile一个Scanner                              │ │
│  │                                                               │ │
│  │  5. 迭代读取                                                  │ │
│  │     ├── 按需从服务器获取数据                                  │ │
│  │     └── 缓存一定数量的行                                      │ │
│  │                                                               │ │
│  │  6. 跨Region继续                                              │ │
│  │     └── 当前Region读完,继续下一个Region                      │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

5.2 Scan配置 #

xml
<!-- hbase-site.xml -->

<!-- 客户端缓存行数 -->
<property>
    <name>hbase.client.scanner.caching</name>
    <value>100</value>
</property>

<!-- Scanner超时时间 -->
<property>
    <name>hbase.client.scanner.timeout.period</name>
    <value>60000</value>
</property>

<!-- Scanner Lease周期 -->
<property>
    <name>hbase.regionserver.lease.period</name>
    <value>60000</value>
</property>

六、数据一致性 #

6.1 MVCC机制 #

text
┌─────────────────────────────────────────────────────────────────────┐
│                         MVCC 机制                                    │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  MVCC(多版本并发控制)保证:                                       │
│  ├── 读写不阻塞                                                     │
│  ├── 读一致性                                                       │
│  └── 写原子性                                                       │
│                                                                     │
│  工作原理:                                                         │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  写操作                                                        │ │
│  │  1. 获取写序号 (writeNum = 5)                                 │ │
│  │  2. 写入MemStore                                              │ │
│  │  3. 更新读点 (readPoint = 5)                                  │ │
│  │                                                               │ │
│  │  读操作                                                        │ │
│  │  1. 获取当前读点 (readPoint = 4)                              │ │
│  │  2. 只读取 writeNum <= readPoint 的数据                       │ │
│  │  3. 不看到进行中的写入                                         │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
│  时间线示例:                                                       │
│  ┌───────────────────────────────────────────────────────────────┐ │
│  │  T1: Write(w=1) ────────────────────────► Commit              │ │
│  │  T2:     Read(r=1) ──────► 看到w=1的数据                       │ │
│  │  T3:          Write(w=2) ──────────────────► Commit           │ │
│  │  T4:              Read(r=1) ──────► 看到w=1的数据(不看到w=2)  │ │
│  │  T5:                   Read(r=2) ──────► 看到w=1,w=2的数据     │ │
│  └───────────────────────────────────────────────────────────────┘ │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

6.2 行级原子性 #

text
行级原子性保证
├── 同一行多个操作的原子性
├── 使用行锁实现
└── 不支持跨行事务(除非使用协处理器)

6.3 一致性级别 #

text
一致性级别
├── 强一致性
│   └── 默认,读写同一Region
│
└── 时间线一致性
    └── 可能读到旧数据(Region迁移场景)

七、性能优化 #

7.1 写入优化 #

text
写入优化策略
├── 批量写入
│   └── 使用批量Put减少网络开销
│
├── 预分区
│   └── 避免Region切分,均匀分布写入
│
├── 调整MemStore
│   ├── 增大MemStore大小
│   └── 调整Flush阈值
│
├── WAL优化
│   ├── 使用异步WAL
│   └── 调整WAL滚动策略
│
└── 压缩
    └── 使用SNAPPY压缩减少IO

7.2 读取优化 #

text
读取优化策略
├── BlockCache调优
│   ├── 增大BlockCache大小
│   └── 使用BucketCache
│
├── Bloom Filter
│   └── 启用布隆过滤器减少IO
│
├── Scan优化
│   ├── 设置合理的缓存大小
│   ├── 指定列族和列
│   └── 使用过滤器
│
├── 避免全表扫描
│   └── 合理设计RowKey
│
└── Compaction
    └── 定期Major Compaction优化查询

7.3 配置示例 #

xml
<!-- 写入优化 -->
<property>
    <name>hbase.hregion.memstore.flush.size</name>
    <value>268435456</value>  <!-- 256MB -->
</property>

<!-- 读取优化 -->
<property>
    <name>hfile.block.cache.size</name>
    <value>0.4</value>
</property>

<!-- Scan优化 -->
<property>
    <name>hbase.client.scanner.caching</name>
    <value>1000</value>
</property>

八、总结 #

本节介绍了HBase的读写流程:

流程 关键步骤
写入 定位Region → 写WAL → 写MemStore → 返回ACK
读取 定位Region → 查BlockCache → 查MemStore → 查HFile → 合并结果
WAL 顺序写入,保证数据持久化
BlockCache 读缓存,加速数据访问
MVCC 多版本并发控制,保证读写一致性
Scan 范围查询,跨Region迭代

下一步,让我们学习HBase的表操作!

最后更新:2026-03-27