Skip to content

项目6 基于HBase实现存储电影网站用户影评分析结果 - 完整实操教程 ​

📚 教程概述 ​

本教程将详细介绍如何使用HBase分布式数据库存储和管理电影用户影评分析结果。通过本教程,你将学会HBase的安装部署、Shell命令操作、Java API编程,并完成一个完整的电影数据存储项目。


第一部分:HBase基础知识 ​

1.1 什么是HBase? ​

HBase的定义:

  • HBase是Apache基于Google BigTable论文实现的开源分布式列式数据库
  • 构建在Hadoop HDFS之上,提供随机、实时读/写访问能力
  • 适合存储非结构化和半结构化的稀疏数据

HBase的起源:

Google BigTable (2006) → HBase (Apache开源实现)
对比项HBaseBigTable
文件存储HDFSGFS
计算框架Java MapReduceC++ MapReduce
协调服务ZooKeeperChubby

1.2 HBase的核心特点 ​

✅ 1. 海量存储 ​

  • 支持PB级数据存储
  • 线性扩展能力
  • 分布式存储架构

✅ 2. 面向列存储 ​

  • 按列族组织数据
  • 列动态可扩展
  • 适合稀疏数据

✅ 3. 多版本支持 ​

  • 每个单元格可保存多个版本
  • 基于时间戳管理版本
  • 支持历史数据追溯

✅ 4. 高可靠性 ​

  • 数据自动复制
  • RegionServer故障自动恢复
  • 支持数据备份

✅ 5. 高性能 ​

  • 毫秒级响应
  • 支持百万级QPS
  • 内存+磁盘混合存储

1.3 HBase vs 传统数据库 ​

对比维度HBase传统关系数据库
数据类型字节数组,无类型限制严格的数据类型
数据操作增删查,不支持复杂事务支持完整ACID事务
存储模式列式存储,稀疏行式存储,密集
数据索引只支持RowKey索引支持多种索引
扩展性水平扩展,线性增长垂直扩展,有限
查询语言无SQL,使用API标准SQL
适用场景大数据实时读写结构化数据管理

1.4 HBase系统架构 ​

┌─────────────────────────────────────────────────┐
│                   Client 客户端                  │
└────────────┬────────────────────────┬───────────┘
             │                        │
             ▼                        ▼
    ┌────────────────┐        ┌────────────────┐
    │   ZooKeeper    │◄──────►│  HMaster主节点  │
    │   协调服务      │        │  (Active/Backup)│
    └────────────────┘        └────────────────┘
             │                        │
             │                        │
    ┌────────▼────────────────────────▼───────────┐
    │         RegionServer集群 (数据节点)          │
    ├──────────────┬──────────────┬───────────────┤
    │ RegionServer1│ RegionServer2│ RegionServer3 │
    │  ┌─────────┐ │  ┌─────────┐ │  ┌─────────┐ │
    │  │ Region  │ │  │ Region  │ │  │ Region  │ │
    │  │ Region  │ │  │ Region  │ │  │ Region  │ │
    │  └─────────┘ │  └─────────┘ │  └─────────┘ │
    └──────────────┴──────────────┴───────────────┘
                      │
                      ▼
              ┌────────────────┐
              │  HDFS 存储层   │
              └────────────────┘

核心组件说明:

  1. Client(客户端)
    • 提供访问HBase的接口
    • 维护缓存加速访问
    • 通过ZooKeeper定位数据
  2. ZooKeeper(协调服务)
    • 存储-ROOT-表位置
    • 监控RegionServer状态
    • 管理HMaster选举
    • 维护集群配置
  3. HMaster(主节点)
    • 管理RegionServer
    • 负责Region分配和负载均衡
    • 处理DDL操作(创建/删除表)
    • 监控集群状态
  4. RegionServer(数据节点)
    • 管理多个Region
    • 处理读写请求
    • 执行Region分裂和合并
    • 维护HLog和MemStore
  5. HDFS(存储层)
    • 持久化存储数据
    • 提供数据冗余
    • 保证数据可靠性

1.5 HBase数据模型 ​

数据模型结构 ​

┌──────────────────────────────────────────────────────────────┐
│                         HBase Table                           │
├────────────┬──────────┬────────────────────────────────────────┤
│  Row Key   │Timestamp │      Column Family                     │
│   (行键)    │  (时间戳) │      (列族)                            │
├────────────┼──────────┼─────────────┬──────────────────────────┤
│            │          │   cf1       │         cf2              │
│            │          ├──────┬──────┼────────┬────────┬────────┤
│            │          │ col1 │ col2 │  col1  │  col2  │  col3  │
├────────────┼──────────┼──────┼──────┼────────┼────────┼────────┤
│   row1     │   t3     │ v1   │  v2  │   v3   │   v4   │   v5   │
│   row2     │   t2     │ v6   │  v7  │   v8   │   v9   │   v10  │
│   row3     │   t1     │ v11  │  v12 │   v13  │   v14  │   v15  │
└────────────┴──────────┴──────┴──────┴────────┴────────┴────────┘

核心概念详解 ​

1. Row Key(行键)

java
// 特点:
- 唯一标识一行数据(类似主键)
- 按字典序排序存储
- 查询的唯一索引
- 设计原则:散列、定长、可读

// 示例:
"user_20241101_001"     // 用户ID + 日期 + 序号
"movie_1234_rating"     // 电影ID + 类型
"2024#device001#log"    // 时间 + 设备 + 类型

2. Column Family(列族)

java
// 特点:
- 建表时必须定义
- 物理存储单元
- 同一列族数据存储在一起
- 数量建议:1-3个

// 示例表结构:
CREATE TABLE 'user_info' {
    NAME => 'basic',     // 基本信息列族
    NAME => 'contact',   // 联系方式列族
    NAME => 'address'    // 地址信息列族
}

3. Column Qualifier(列限定符)

java
// 特点:
- 列族下的具体列
- 无需预先定义
- 动态添加
- 格式:列族:列限定符

// 示例:
basic:name       // 基本信息:姓名
basic:age        // 基本信息:年龄
contact:phone    // 联系方式:电话
contact:email    // 联系方式:邮箱

4. Timestamp(时间戳)

java
// 特点:
- 每个单元格的版本标识
- 默认为写入时间(毫秒)
- 支持多版本数据
- 可自定义时间戳

// 查询指定版本:
get 'table', 'row1', {COLUMN => 'cf:col', TIMESTAMP => 1234567890}

// 查询所有版本:
get 'table', 'row1', {COLUMN => 'cf:col', VERSIONS => 5}

5. Cell(单元格)

java
// 完整定位:
{RowKey, ColumnFamily, Column, Timestamp} → Value

// 示例:
{
    "user001",           // Row Key
    "basic:name",        // Column Family:Column
    1234567890,          // Timestamp
    "张三"                // Value
}

数据模型示例 ​

用户信息表设计:

Table: user_profile
RowKey: user_id

Row Key    | Timestamp |  basic:name  | basic:age | contact:phone | contact:email
-----------|-----------|--------------|-----------|---------------|---------------
user_001   | t3        | 张三          | 25        | 138xxx        | zhang@xx.com
           | t2        | 张三          | 24        | 138xxx        | zhang@xx.com
           | t1        | 张小三        | 24        | 138xxx        | zhang@xx.com
user_002   | t2        | 李四          | 30        | 139xxx        | li@xx.com
user_003   | t1        | 王五          | 28        | 137xxx        | wang@xx.com

1.6 HBase读写流程 ​

写流程详解 ​

┌─────────┐
│ Client  │
└────┬────┘
     │ 1. 请求写入数据
     ▼
┌─────────────┐
│ ZooKeeper   │
└────┬────────┘
     │ 2. 返回.meta.表位置
     ▼
┌─────────────────┐
│ .meta. Region   │
│ Server          │
└────┬────────────┘
     │ 3. 返回目标Region位置
     ▼
┌─────────────────────────────────┐
│   Target RegionServer           │
│  ┌──────────────────────────┐   │
│  │  1. 写入HLog (WAL)       │   │ 4. 写入数据
│  │     ↓                    │   │
│  │  2. 写入MemStore(内存)   │   │
│  │     ↓                    │   │
│  │  3. MemStore满→刷写      │   │
│  │     ↓                    │   │
│  │  4. 生成HFile存储到HDFS  │   │
│  └──────────────────────────┘   │
└─────────────────────────────────┘
     │ 5. 返回写入成功
     ▼
┌─────────┐
│ Client  │
└─────────┘

写流程步骤:

  1. 客户端发起请求

    • Client向ZooKeeper查询.meta.表位置
    • 获取目标数据的Region信息
  2. 定位目标Region

    • 通过.meta.表找到目标RegionServer
    • 缓存Region位置信息
  3. 写入数据

    a. 先写HLog(预写日志)
       - 保证数据持久性
       - 用于故障恢复
    
    b. 写入MemStore(内存)
       - 内存中的排序缓冲区
       - 提供快速写入
    
    c. 返回客户端成功
       - 异步刷写到磁盘
  4. 刷写机制

    触发条件:
    - MemStore达到阈值(默认128MB)
    - Region中所有MemStore达到上限
    - 定期刷写(默认1小时)
    - 手动flush命令
    
    刷写过程:
    MemStore → HFile → HDFS

读流程详解 ​

┌─────────┐
│ Client  │
└────┬────┘
     │ 1. 请求读取数据
     ▼
┌─────────────┐
│ ZooKeeper   │
└────┬────────┘
     │ 2. 返回.meta.表位置
     ▼
┌─────────────────┐
│ .meta. Region   │
│ Server          │
└────┬────────────┘
     │ 3. 返回目标Region位置
     ▼
┌──────────────────────────────────────┐
│   Target RegionServer                │
│  ┌───────────────────────────────┐   │
│  │  读取顺序:                    │   │ 4. 读取数据
│  │  1. BlockCache (读缓存)       │   │
│  │  2. MemStore (内存)           │   │
│  │  3. HFile (磁盘)              │   │
│  │                               │   │
│  │  合并结果 → 返回最新版本      │   │
│  └───────────────────────────────┘   │
└──────────────────────────────────────┘
     │ 5. 返回查询结果
     ▼
┌─────────┐
│ Client  │
└─────────┘

读流程步骤:

  1. 客户端发起请求

    • 向ZooKeeper查询.meta.表
    • 获取目标Region位置
  2. 定位数据位置

    • 通过.meta.表找到RegionServer
    • 缓存位置信息
  3. 多层查询

    a. BlockCache(读缓存)
       - LRU缓存最近读取的Block
       - 命中则直接返回
    
    b. MemStore(内存存储)
       - 查询未刷写的新数据
       - 合并到结果中
    
    c. HFile(磁盘文件)
       - 通过Bloom Filter过滤
       - 查询磁盘数据
       - 加载到BlockCache
  4. 合并返回

    • 合并多个来源的数据
    • 按时间戳返回最新版本
    • 缓存查询结果

1.7 .meta.表详解 ​

.meta.表结构:

RowKey: TableName,StartKey,Timestamp

Columns:
├── info:regioninfo    // Region详细信息
│   └── StartKey, EndKey, Family列表
├── info:server        // RegionServer地址
│   └── hostname:port
└── info:serverstartcode // RegionServer启动时间

示例:

RowKey: film_ratings,,1234567890
├── info:regioninfo → {StartKey='', EndKey='movie_1000', families=['cf']}
├── info:server → slave1:16020
└── info:serverstartcode → 1234567890000

第二部分:ZooKeeper安装配置 ​

2.1 为什么需要ZooKeeper? ​

ZooKeeper在HBase中的作用:

  1. Master选举
    • 保证唯一活跃Master
    • Master故障时自动选举
    • 避免脑裂问题
  2. RegionServer监控
    • 实时监控RegionServer状态
    • 节点故障及时发现
    • 触发故障转移
  3. 元数据存储
    • 存储-ROOT-表位置
    • 维护集群配置
    • 管理Region分配信息
  4. 分布式协调
    • 提供分布式锁
    • 实现配置同步
    • 保证数据一致性

2.2 ZooKeeper集群角色 ​

┌────────────────────────────────────────┐
│         ZooKeeper Cluster              │
├────────────────────────────────────────┤
│                                        │
│   ┌──────────┐    ┌──────────┐        │
│   │  Leader  │◄──►│ Follower │        │
│   │  (领导者) │    │  (跟随者) │        │
│   └────┬─────┘    └────┬─────┘        │
│        │               │              │
│        │    ┌──────────▼────┐         │
│        └───►│   Follower    │         │
│             │   (跟随者)     │         │
│             └──────┬────────┘         │
│                    │                  │
│             ┌──────▼────────┐         │
│             │   Observer    │         │
│             │   (观察者)     │         │
│             └───────────────┘         │
└────────────────────────────────────────┘
角色职责特点
Leader处理写请求、发起投票唯一,通过选举产生
Follower处理读请求、参与投票多个,参与选举
Observer处理读请求、不参与投票扩展读能力,不影响写性能

2.3 ZooKeeper选举机制 ​

选举涉及的概念:

  1. 服务器ID(myid)
    • 每个节点的唯一标识
    • 配置在dataDir/myid文件中
    • ID越大优先级越高
  2. 选举状态
    • LOOKING:选举状态
    • FOLLOWING:跟随者状态
    • LEADING:领导者状态
    • OBSERVING:观察者状态
  3. 数据ID(ZXID)
    • 事务ID,64位数字
    • 高32位:epoch(选举轮次)
    • 低32位:递增计数器
    • ZXID越大数据越新
  4. 逻辑时钟(Epoch)
    • 投票轮次
    • 每次选举递增
    • 用于区分不同轮次的投票

选举过程示例:

初始状态:3个节点启动
myid: server1=1, server2=2, server3=3

第1轮投票:
server1: 投票给自己 (1, ZXID1)
server2: 投票给自己 (2, ZXID2)  
server3: 投票给自己 (3, ZXID3)

比较规则:
1. 先比较ZXID,大的胜出
2. ZXID相同,比较myid,大的胜出

第2轮投票(假设ZXID相同):
server1: 改投server3 (因为3 > 1)
server2: 改投server3 (因为3 > 2)
server3: 仍投自己

最终结果:
server3获得3票(包括自己) > 半数(2)
server3成为Leader
server1、server2成为Follower

2.4 ZooKeeper安装部署(实操) ​

​


第七部分:性能优化与最佳实践 ​

7.1 RowKey设计原则 ​

原则1:避免热点问题 ​

❌ 错误设计(顺序RowKey):

user_20240101_001
user_20240101_002
user_20240101_003
...
↓ 问题:所有数据写入同一个Region

✅ 正确设计(散列RowKey):

方法1:加盐(Salting)
hash(user_id)%10 + "_" + user_id + "_" + timestamp
例如:3_user001_20240101

方法2:反转(Reversing)
20240101 → 10140202

原则2:定长设计 ​

java
// ✅ 推荐:定长RowKey
String rowKey = String.format("%010d_%s", userId, timestamp);
// 结果:0000001234_20240101

// ❌ 不推荐:变长RowKey
String rowKey = userId + "_" + timestamp;
// 结果:1234_20240101(长度不一致)

原则3:可读性 ​

业务前缀 + 时间戳 + 唯一ID
order_20240101_12345678
movie_2024_action_001
user_north_beijing_001

7.2 列族设计原则 ​

原则1:列族数量要少 ​

✅ 推荐:1-3个列族
CREATE TABLE 'user', 'basic', 'contact'

❌ 不推荐:过多列族
CREATE TABLE 'user', 'f1', 'f2', 'f3', 'f4', 'f5', ...

原因:

  • 每个列族对应一个Store
  • 过多列族增加RegionServer负担
  • 影响flush和compaction性能

原则2:按访问频率划分 ​

CREATE TABLE 'article',
  {NAME => 'meta', TTL => 7776000},      # 元数据,保留90天
  {NAME => 'content', TTL => 2592000}    # 内容,保留30天

7.3 批量操作优化 ​

java
// ❌ 逐条插入(慢)
for (int i = 0; i < 10000; i++) {
    Put put = new Put(Bytes.toBytes("row" + i));
    put.addColumn(...);
    table.put(put);  // 每次都发送请求
}

// ✅ 批量插入(快)
List<Put> puts = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
    Put put = new Put(Bytes.toBytes("row" + i));
    put.addColumn(...);
    puts.add(put);
}
table.put(puts);  // 一次性发送所有请求

7.4 缓存优化 ​

java
// 设置Scan缓存
Scan scan = new Scan();
scan.setCaching(1000);  // 每次RPC获取1000行
scan.setBatch(100);     // 每行最多返回100列

// 使用BlockCache
HColumnDescriptor cf = new HColumnDescriptor("cf");
cf.setBlockCacheEnabled(true);  // 启用BlockCache
cf.setInMemory(true);          // 常驻内存(慎用)

7.5 预分区策略 ​

java
// 创建表时预分区
byte[][] splits = new byte[][] {
    Bytes.toBytes("row100"),
    Bytes.toBytes("row200"),
    Bytes.toBytes("row300"),
    ...
};
admin.createTable(tableDescriptor, splits);

作用:

  • 避免Region自动分裂
  • 数据均匀分布
  • 提高并发写入性能

7.6 Compaction策略 ​

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

<!-- Minor Compaction间隔 -->
<property>
    <name>hbase.hregion.majorcompaction</name>
    <value>604800000</value>  <!-- 7天 -->
</property>

<!-- 禁用自动Major Compaction -->
<property>
    <name>hbase.hregion.majorcompaction</name>
    <value>0</value>
</property>

手动触发:

bash
# Minor Compaction
compact 'table_name'

# Major Compaction(慎用,影响性能)
major_compact 'table_name'

第八部分:常见问题与解决方案 ​

8.1 连接问题 ​

问题1:无法连接ZooKeeper ​

错误信息:

org.apache.zookeeper.KeeperException$ConnectionLossException

解决方案:

bash
# 1. 检查ZooKeeper是否启动
zkServer.sh status

# 2. 检查防火墙
systemctl status firewalld
firewall-cmd --zone=public --add-port=2181/tcp --permanent

# 3. 检查hosts文件
vim /etc/hosts
# 确保包含:
192.168.128.131 slave1
192.168.128.132 slave2
192.168.128.133 slave3

# 4. 验证连接
zkCli.sh -server slave1:2181

问题2:HBase启动失败 ​

错误信息:

HMaster: Failed to become active master

解决方案:

bash
# 1. 检查HDFS是否启动
hdfs dfsadmin -report

# 2. 检查ZooKeeper连接
hbase zkcli
ls /hbase

# 3. 清理ZooKeeper中的HBase数据
deleteall /hbase

# 4. 重新启动HBase
stop-hbase.sh
start-hbase.sh

8.2 性能问题 ​

问题3:写入速度慢 ​

诊断:

bash
# 查看RegionServer日志
tail -f $HBASE_HOME/logs/hbase-*-regionserver-*.log

优化方案:

java
// 1. 关闭WAL(风险:数据可能丢失)
Put put = new Put(Bytes.toBytes(rowKey));
put.setDurability(Durability.SKIP_WAL);

// 2. 使用批量插入
List<Put> puts = new ArrayList<>();
// ... 添加Put
table.put(puts);

// 3. 增加MemStore大小
<property>
    <name>hbase.hregion.memstore.flush.size</name>
    <value>268435456</value>  <!-- 256MB -->
</property>

问题4:读取速度慢 ​

优化方案:

java
// 1. 使用Bloom Filter
HColumnDescriptor cf = new HColumnDescriptor("cf");
cf

基于 Vite 强力驱动 | 纯静态轻量托管