HBase

Apache HBase 是基于 HDFS分布式列式 NoSQL 数据库,灵感来自 Google Bigtable,提供对数十亿行 × 数百万列数据的毫秒级随机读写能力。适合稀疏大表、时序数据、日志存储、消息归档等场景。


数据模型

HBase 以四维坐标定位一条数据:(RowKey, ColumnFamily:Qualifier, Timestamp) → Value

Table: user_behavior
┌──────────────┬──────────────────────────────┬──────────────────────┐
│   RowKey     │   Column Family: info        │  Column Family: act  │
│              │  name      │  city           │  click  │  buy       │
├──────────────┼────────────┼─────────────────┼─────────┼────────────┤
│ user_001     │  Alice     │  Beijing        │  12     │  3         │
│ user_002     │  Bob       │  Shanghai       │  8      │            │  ← 稀疏
└──────────────┴────────────┴─────────────────┴─────────┴────────────┘
概念说明
RowKey行键,全表唯一,字节序排序,是唯一索引
Column Family列族,建表时定义,物理存储在同一文件
Qualifier列名,动态添加,数量不限
Timestamp版本号(通常为毫秒时间戳),支持多版本查询
Cell一个具体的值(RowKey + CF + Qualifier + Timestamp)

架构

HMaster(管理节点)
  ├── Region 分配 / 迁移
  ├── DDL(建表/删表)
  └── 监控 RegionServer

RegionServer(数据节点,多个)
  └── Region(一段连续 RowKey 范围)
        └── Store(每个 Column Family 对应一个)
              ├── MemStore(写缓冲,内存)
              └── HFile(SSTable,持久化到 HDFS)

ZooKeeper
  └── 存储 HMaster 地址、RegionServer 心跳、Meta 表位置

写流程:WAL(预写日志)→ MemStore → MemStore 满 flush 为 HFile → 后台 Compaction 合并 HFile。

读流程:BlockCache → MemStore → HFile(布隆过滤器加速定位)。


快速上手(Java API)

<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>2.6.0</version>
</dependency>
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "zk1,zk2,zk3");
conf.set("hbase.zookeeper.property.clientPort", "2181");
 
try (Connection connection = ConnectionFactory.createConnection(conf);
     Table table = connection.getTable(TableName.valueOf("user_behavior"))) {
 
    // PUT(写入)
    Put put = new Put(Bytes.toBytes("user_001"));
    put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"),  Bytes.toBytes("Alice"));
    put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("city"),  Bytes.toBytes("Beijing"));
    put.addColumn(Bytes.toBytes("act"),  Bytes.toBytes("click"), Bytes.toBytes("12"));
    table.put(put);
 
    // GET(按 RowKey 点查)
    Get get = new Get(Bytes.toBytes("user_001"));
    get.addFamily(Bytes.toBytes("info"));           // 只取 info 列族
    Result result = table.get(get);
    String name = Bytes.toString(result.getValue(
        Bytes.toBytes("info"), Bytes.toBytes("name")));
 
    // SCAN(范围扫描)
    Scan scan = new Scan();
    scan.withStartRow(Bytes.toBytes("user_001"));
    scan.withStopRow(Bytes.toBytes("user_010"));    // 左闭右开
    scan.addColumn(Bytes.toBytes("act"), Bytes.toBytes("buy"));
    scan.setCaching(100);                            // 每次 RPC 批量返回 100 行
    try (ResultScanner scanner = table.getScanner(scan)) {
        for (Result r : scanner) {
            System.out.println(Bytes.toString(r.getRow()));
        }
    }
 
    // DELETE
    Delete delete = new Delete(Bytes.toBytes("user_001"));
    delete.addColumn(Bytes.toBytes("act"), Bytes.toBytes("click"));
    table.delete(delete);
}

DDL(建表)

try (Admin admin = connection.getAdmin()) {
    TableName tableName = TableName.valueOf("user_behavior");
 
    ColumnFamilyDescriptor infoFamily = ColumnFamilyDescriptorBuilder
        .newBuilder(Bytes.toBytes("info"))
        .setMaxVersions(1)
        .setCompressionType(Compression.Algorithm.SNAPPY)  // 生产必开
        .build();
 
    ColumnFamilyDescriptor actFamily = ColumnFamilyDescriptorBuilder
        .newBuilder(Bytes.toBytes("act"))
        .setMaxVersions(3)         // 保留 3 个历史版本
        .setBloomFilterType(BloomType.ROW)  // 加速 GET 查询
        .build();
 
    TableDescriptor tableDesc = TableDescriptorBuilder
        .newBuilder(tableName)
        .setColumnFamily(infoFamily)
        .setColumnFamily(actFamily)
        .build();
 
    // 预分区(避免热点写入同一 Region)
    byte[][] splitKeys = {
        Bytes.toBytes("user_200"),
        Bytes.toBytes("user_400"),
        Bytes.toBytes("user_600"),
        Bytes.toBytes("user_800"),
    };
    admin.createTable(tableDesc, splitKeys);
}

RowKey 设计

RowKey 是 HBase 唯一的索引,设计好坏直接影响性能:

热点问题:顺序递增 RowKey(如时间戳前缀)导致写入集中在最后一个 Region。

方案说明示例
加盐(Salting)前缀加随机数 / 哈希前几位3_user_0017_user_001
翻转(Reversing)翻转单调部分10000000033000000001
哈希前缀MD5/CRC32 取前几字节ab3f_user_001
组合键业务维度 + 时间userId_reverseTimestamp

时序数据常用模式userId + (Long.MAX_VALUE - timestamp) → 按用户分片,同用户内按时间倒序,最新数据排在前面,Scan 无需 LIMIT 即可取最新 N 条。


过滤器

// 单列值过滤
Filter filter = new SingleColumnValueFilter(
    Bytes.toBytes("info"), Bytes.toBytes("city"),
    CompareOperator.EQUAL, Bytes.toBytes("Beijing"));
 
// 前缀过滤(RowKey 前缀匹配)
Filter prefixFilter = new PrefixFilter(Bytes.toBytes("user_"));
 
// 分页过滤(结合 lastRow 实现翻页)
Filter pageFilter = new PageFilter(100);
 
// 组合过滤
FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
filterList.addFilter(prefixFilter);
filterList.addFilter(filter);
 
scan.setFilter(filterList);

HBase vs 关系型数据库

维度HBaseMySQL
数据模型列式、稀疏、多版本行式、关系型
Schema列族固定,列动态强 Schema
索引仅 RowKey主键 + 多个二级索引
事务单行原子,无跨行事务完整 ACID
查询语言Java API / 有限 SQL(Phoenix)SQL
扩展性水平扩展,PB 级垂直扩展为主
适用场景大规模稀疏数据、时序、日志业务数据、复杂查询

相关链接

  • HDFS — HBase 底层文件系统
  • Hadoop — HBase 依赖 Hadoop 生态
  • Hive — 与 HBase 可集成,通过 HiveQL 分析 HBase 数据
  • 大数据 ← 返回大数据目录