Skip to content

HBase 与 Hive

Hadoop 生态系统不仅包含 MapReduce 和 HDFS,还发展出了丰富的数据存储和查询工具。HBase 提供实时读写的列式存储,Hive 提供类 SQL 的批处理查询接口,两者分别面向不同的应用场景。

HBase 列式存储

设计动机

关系数据库(RDBMS)在面对海量数据时存在根本性局限:单表难以存储数百 GB 数据,修改大表结构代价高昂,难以在线扩容。更重要的是,根据 CAP 定理,分布式系统无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),而 RDBMS 优先保证一致性,在扩展性上做出妥协。

HBase 是 Google BigTable 的开源实现,设计目标是在 HDFS 之上提供一个分布式列式存储系统,能够管理数十亿行、百万列级别的结构化数据,并提供实时的随机读写能力。

数据模型

HBase 的数据模型是一个多维的有序映射表,由以下要素定位一个数据单元:

要素 说明 特点
RowKey 行主键 按字典序排列,设计好坏直接影响查询性能
Column Family 列族 必须预先定义,是访问控制和存储管理的基本单位
Column Qualifier 列限定符 隶属于某个列族,可以动态添加
Timestamp 时间戳 支持多版本数据,默认自动递增
Cell 单元格 由上述四者唯一确定,存储实际的字节数据
RowKey    CF:qualifier    timestamp    value
─────────────────────────────────────────────
row1      info:name       t3           "Alice"
row1      info:name       t2           "Bob"      (历史版本)
row1      score:math      t1           95
row2      info:name       t1           "Charlie"

物理存储架构

graph TB
    subgraph HBase Cluster
        Master[HMaster] --> RS1[RegionServer 1]
        Master --> RS2[RegionServer 2]
        Master --> RS3[RegionServer 3]

        subgraph RS1
            R1[Region 1] --> Store1[Store - CF:info]
            R1 --> Store2[Store - CF:score]
            Store1 --> MS1[MemStore]
            Store1 --> SF1[StoreFile / HFile]
        end
    end

    RS1 --- HDFS[(HDFS)]
    RS2 --- HDFS
    RS3 --- HDFS
  • Region:大表按 RowKey 范围分割为多个 Region,每个 Region 存储在一个 RegionServer 上
  • MemStore:内存中的写缓冲区,数据写入时先到 MemStore
  • StoreFile(HFile):MemStore 满后刷写到磁盘,形成不可变的 HFile 文件
  • WAL(Write Ahead Log):写入前先记录日志,保证数据不丢失

数据读取时先查 MemStore,再查磁盘上的 StoreFile(每个 StoreFile 有类似 B 树的结构,允许快速查询)。多个小的 StoreFile 会定期合并(Compaction),Region 过大时会自动分裂(Split)。

数据记录的定位(二级索引)

HBase 使用二级索引结构定位数据记录:

  1. 第一层:ZooKeeper 中保存 hbase:meta 表(旧版本称 .META.)所在的 RegionServer 地址
  2. 第二层hbase:meta 表保存所有用户数据表的 Region 位置信息(哪个 RowKey 范围在哪个 RegionServer 上)

版本说明:HBase 0.96 之前采用三级索引(ZooKeeper → -ROOT- Region → .META. → 用户数据),但由于 .META. 表极少需要分裂,-ROOT- 这一层实际意义不大,0.96 版本将其移除,简化为二级索引。

客户端查询时,先从 ZooKeeper 获取 hbase:meta 的位置,再查 hbase:meta 表找到目标 Region 所在的 RegionServer,最后直接访问该 RegionServer 读取数据。客户端会缓存 Region 位置信息,后续查询可跳过前两步。

Java API 示例

创建表

Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "zk-host");
HBaseAdmin admin = new HBaseAdmin(conf);

HTableDescriptor table = new HTableDescriptor("students");
table.addFamily(new HColumnDescriptor("info"));
table.addFamily(new HColumnDescriptor("score"));
admin.createTable(table);

插入数据

HTable table = new HTable(conf, "students");
Put put = new Put(Bytes.toBytes("row1"));
put.add(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("Alice"));
put.add(Bytes.toBytes("score"), Bytes.toBytes("math"), Bytes.toBytes("95"));
table.put(put);

查询数据

Get get = new Get(Bytes.toBytes("row1"));
get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"));
Result result = table.get(get);
byte[] value = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("name"));
System.out.println(Bytes.toString(value));  // "Alice"

RowKey 设计原则

RowKey 的设计对 HBase 性能至关重要:

  • 避免单调递增:如使用时间戳作为 RowKey,会导致写入热点集中在单个 Region
  • 散列化:对原始 RowKey 取哈希或反转,使数据均匀分布
  • 考虑查询模式:将经常一起访问的数据放在相邻的 RowKey 位置(空间局部性)
  • 长度控制:RowKey 会存储在每个 KeyValue 中,过长会浪费存储空间

Hive 数据仓库

设计动机

HDFS 和 MapReduce 虽然强大,但直接编写 Java 程序进行数据分析门槛太高。数据分析师习惯使用 SQL 进行查询,Hive 的出现正是为了在 Hadoop 之上提供一个类 SQL 的查询接口。

Hive 最初由 Facebook 开发,用于处理每天数十 TB 的日志数据。它将 HQL(Hive SQL)查询自动编译为 MapReduce 作业执行,让用户无需编写 Java 代码即可完成大规模数据分析。

系统架构

graph LR
    User[用户] --> CLI[CLI / JDBC / ODBC]
    CLI --> Driver[Driver]
    Driver --> Compiler[Compiler]
    Compiler --> EE[Execution Engine]
    EE --> HDFS[(HDFS)]
    EE --> MR[MapReduce]
    Driver --> MS[(Metastore)]
组件 功能
HiveQL 类 SQL 查询语言,提供数据定义和操作接口
Driver 驱动程序,协调各组件工作
Compiler 将 HQL 编译为 MapReduce 执行计划
Execution Engine 执行编译后的计划,调用 MapReduce 或 HDFS 操作
Metastore 元数据存储,保存表结构、分区信息、存储位置等

数据模型

Hive 的数据模型包含以下层次:

  • Tables:类似关系数据库的表,列有类型(int、string、float 等),也支持复合类型(list、map、struct)
  • Partitions:按某个列(如日期)将表数据划分为不同的目录,提高查询效率
  • Buckets:在 Partition 内按哈希值进一步分桶,有利于抽样查询和 Join 优化

数据物理存储在 HDFS 的 /home/hive/warehouse 目录下,每个表对应一个子目录,Partition 和 Bucket 形成更深层的子目录。

表类型

Hive 支持三种表类型:

内部表(Managed Table):Hive 管理数据的生命周期,删除表时数据也被删除。

CREATE TABLE logs (ts BIGINT, msg STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE;

外部表(External Table):数据存储在 Hive 之外,删除表时只删除元数据,不影响实际数据。

CREATE EXTERNAL TABLE access_log (ip STRING, url STRING)
LOCATION '/user/data/access_log';

分区表(Partitioned Table):按分区键组织数据,查询时可只扫描相关分区。

CREATE TABLE pageviews (url STRING, count INT)
PARTITIONED BY (dt STRING)
STORED AS ORC;

HQL 示例

-- 创建表
CREATE TABLE shakespeare (freq INT, word STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE;

-- 加载数据
LOAD DATA INPATH 'shakespeare_freq' INTO TABLE shakespeare;

-- 查询
SELECT * FROM shakespeare WHERE freq > 100
ORDER BY freq ASC LIMIT 10;

-- 聚合
INSERT OVERWRITE TABLE word_stats
SELECT word, SUM(freq) FROM shakespeare GROUP BY word;

-- Join
SELECT a.name, b.order_amount
FROM customers a JOIN orders b ON a.id = b.customer_id;

存储格式

Hive 支持多种存储格式,对查询性能影响显著:

格式 特点 适用场景
TextFile 纯文本,默认格式,不可分割压缩 小数据量、调试
SequenceFile 二进制键值对,可分割压缩 中间数据
ORC 列式存储,内置索引和压缩 大规模分析查询
Parquet 列式存储,跨平台兼容 与 Spark 等引擎配合

列式存储(ORC、Parquet)的优势在于:查询时只读取需要的列,减少 I/O;同一列数据类型相同,压缩率更高。

HBase vs Hive

维度 HBase Hive
定位 实时读写的列式存储 批处理数据仓库
查询延迟 毫秒级 分钟到小时级
数据模型 NoSQL,半结构化 类关系数据库,结构化
查询语言 Java API / Shell HQL(类 SQL)
底层存储 HDFS HDFS
典型场景 实时查询、随机读写 日志分析、报表统计

两者可以配合使用:HBase 存储实时数据,Hive 提供基于 HBase 表的 SQL 查询能力,实现实时存储与批量分析的统一。