Skip to content

Hive 数据仓库

什么是 Hive

Hive 是基于 Hadoop 的数据仓库工具,由 Facebook 开源。它将结构化数据文件映射为数据库表,并提供 HiveQL(类 SQL 查询语言),将 SQL 翻译成 MapReduce/Tez/Spark 任务执行。

核心价值:让不懂 MapReduce 的数据分析师也能处理海量数据。

适用场景

  • 离线数仓建设(T+1 报表)
  • 大规模数据 ETL
  • 数据探索与分析
  • 不适合:实时查询、随机读写、OLTP

架构设计

┌─────────────────────────────────────────────────────────────┐
│                      Hive 架构                               │
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │                   用户接口                            │  │
│  │   CLI(hive shell)  JDBC/ODBC  HiveServer2  WebUI   │  │
│  └──────────────────────────┬───────────────────────────┘  │
│                             │                               │
│  ┌──────────────────────────▼───────────────────────────┐  │
│  │                   Driver(驱动层)                    │  │
│  │   解析器(Parser)→ 编译器(Compiler)→ 优化器        │  │
│  │   → 执行器(Executor)                               │  │
│  └──────────────────────────┬───────────────────────────┘  │
│                             │                               │
│  ┌──────────────────────────▼───────────────────────────┐  │
│  │                   Metastore(元数据)                 │  │
│  │   表结构、分区信息、列信息 → 存储在 MySQL/Derby       │  │
│  └──────────────────────────┬───────────────────────────┘  │
│                             │                               │
│  ┌──────────────────────────▼───────────────────────────┐  │
│  │              执行引擎(MapReduce / Tez / Spark)      │  │
│  └──────────────────────────┬───────────────────────────┘  │
│                             │                               │
│  ┌──────────────────────────▼───────────────────────────┐  │
│  │                      HDFS                            │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

数据仓库分层

Hive 通常按照以下分层架构组织数据:

ODS(操作数据层)
  原始数据,直接从业务系统同步,不做处理
  保留原始格式,通常按天分区

DWD(数据明细层)
  对 ODS 数据进行清洗、标准化
  去除脏数据,统一字段格式

DWS(数据汇总层)
  按主题对 DWD 数据进行轻度聚合
  例:用户每日行为汇总、商品每日销售汇总

ADS(应用数据层)
  面向具体业务需求的数据集市
  直接供 BI 报表、API 使用

HiveQL 核心语法

DDL 操作

sql
-- 创建数据库
CREATE DATABASE IF NOT EXISTS dw_db
COMMENT '数据仓库'
LOCATION '/user/hive/warehouse/dw_db.db';

-- 创建内部表(Managed Table)
-- 删除表时,数据也会被删除
CREATE TABLE IF NOT EXISTS ods_user_log (
    user_id     STRING COMMENT '用户ID',
    event_type  STRING COMMENT '事件类型',
    event_time  BIGINT COMMENT '事件时间戳',
    page_url    STRING COMMENT '页面URL',
    ip          STRING COMMENT 'IP地址'
)
COMMENT '用户行为日志原始表'
PARTITIONED BY (dt STRING COMMENT '日期分区')
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS ORC
TBLPROPERTIES ('orc.compress' = 'SNAPPY');

-- 创建外部表(External Table)
-- 删除表时,数据不会被删除(推荐用于 ODS 层)
CREATE EXTERNAL TABLE ods_order (
    order_id    STRING,
    user_id     STRING,
    amount      DECIMAL(10,2),
    status      INT,
    create_time STRING
)
PARTITIONED BY (dt STRING)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
STORED AS TEXTFILE
LOCATION '/data/ods/order/';

-- 添加分区
ALTER TABLE ods_order ADD PARTITION (dt='2024-01-01')
LOCATION '/data/ods/order/dt=2024-01-01';

-- 修复分区(自动发现 HDFS 上的分区目录)
MSCK REPAIR TABLE ods_order;

DML 操作

sql
-- 插入数据(覆盖写)
INSERT OVERWRITE TABLE dwd_user_log PARTITION (dt='2024-01-01')
SELECT
    user_id,
    event_type,
    FROM_UNIXTIME(event_time, 'yyyy-MM-dd HH:mm:ss') AS event_time,
    page_url,
    ip
FROM ods_user_log
WHERE dt = '2024-01-01'
  AND user_id IS NOT NULL
  AND event_type IN ('click', 'view', 'purchase');

-- 动态分区插入
SET hive.exec.dynamic.partition = true;
SET hive.exec.dynamic.partition.mode = nonstrict;

INSERT OVERWRITE TABLE dwd_user_log PARTITION (dt)
SELECT user_id, event_type, event_time, page_url, ip, dt
FROM ods_user_log
WHERE dt >= '2024-01-01';

-- 多表插入(一次扫描,写入多张表)
FROM ods_user_log
INSERT OVERWRITE TABLE dwd_click_log PARTITION (dt='2024-01-01')
    SELECT user_id, page_url, event_time WHERE event_type = 'click'
INSERT OVERWRITE TABLE dwd_purchase_log PARTITION (dt='2024-01-01')
    SELECT user_id, amount, event_time WHERE event_type = 'purchase';

常用查询

sql
-- 窗口函数
SELECT
    user_id,
    event_time,
    event_type,
    -- 用户当天第几次行为
    ROW_NUMBER() OVER (PARTITION BY user_id, dt ORDER BY event_time) AS rn,
    -- 用户累计行为次数
    COUNT(*) OVER (PARTITION BY user_id ORDER BY event_time
                   ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cum_cnt,
    -- 上一次行为时间
    LAG(event_time, 1) OVER (PARTITION BY user_id ORDER BY event_time) AS prev_time
FROM dwd_user_log
WHERE dt = '2024-01-01';

-- 用户留存率计算
SELECT
    a.dt AS first_day,
    COUNT(DISTINCT a.user_id) AS day0_users,
    COUNT(DISTINCT b.user_id) AS day1_users,
    ROUND(COUNT(DISTINCT b.user_id) / COUNT(DISTINCT a.user_id), 4) AS retention_rate
FROM (
    SELECT user_id, dt FROM dwd_user_log GROUP BY user_id, dt
) a
LEFT JOIN (
    SELECT user_id, dt FROM dwd_user_log GROUP BY user_id, dt
) b ON a.user_id = b.user_id
   AND DATEDIFF(b.dt, a.dt) = 1
WHERE a.dt = '2024-01-01'
GROUP BY a.dt;

存储格式

格式特点适用场景
TextFile文本,可读,无压缩原始数据导入(ODS)
SequenceFile二进制,支持压缩中间结果
ORC列式,高压缩,支持索引数仓(推荐)
Parquet列式,与 Spark 生态兼容好数仓、数据湖
Avro行式,Schema 演进好数据序列化

生产推荐:ORC + SNAPPY 压缩(Hive 生态)或 Parquet + SNAPPY(Spark 生态)

sql
-- 创建 ORC 表
CREATE TABLE dws_user_daily (
    user_id     STRING,
    pv          BIGINT,
    uv          BIGINT,
    purchase_cnt BIGINT,
    total_amount DECIMAL(12,2)
)
PARTITIONED BY (dt STRING)
STORED AS ORC
TBLPROPERTIES (
    'orc.compress' = 'SNAPPY',
    'orc.stripe.size' = '67108864',
    'orc.row.index.stride' = '10000'
);

性能调优

开启 Tez 引擎

bash
# hive-site.xml 或 session 级别设置
SET hive.execution.engine=tez;  # 比 MapReduce 快 2-10x
# 或
SET hive.execution.engine=spark;

分区裁剪

sql
-- 必须在 WHERE 中指定分区条件,否则全表扫描
SELECT * FROM ods_user_log WHERE dt = '2024-01-01';  -- ✅ 分区裁剪
SELECT * FROM ods_user_log WHERE event_type = 'click'; -- ❌ 全表扫描

处理数据倾斜

sql
-- 开启 Map 端 Join(小表 < 25MB)
SET hive.auto.convert.join = true;
SET hive.mapjoin.smalltable.filesize = 26214400;

-- 开启倾斜 Join
SET hive.optimize.skewjoin = true;
SET hive.skewjoin.key = 100000;

-- 手动处理倾斜:给倾斜 key 加随机前缀
SELECT
    REGEXP_REPLACE(a.user_id, '^[0-9]+_', '') AS user_id,
    a.cnt
FROM (
    SELECT
        CONCAT(CAST(FLOOR(RAND() * 10) AS STRING), '_', user_id) AS user_id,
        COUNT(*) AS cnt
    FROM dwd_user_log
    GROUP BY CONCAT(CAST(FLOOR(RAND() * 10) AS STRING), '_', user_id)
) a;

合并小文件

sql
-- 输出时合并小文件
SET hive.merge.mapfiles = true;
SET hive.merge.mapredfiles = true;
SET hive.merge.size.per.task = 256000000;  -- 256MB
SET hive.merge.smallfiles.avgsize = 16000000;  -- 16MB

关键配置

sql
-- 并行执行(无依赖的 Stage 并行跑)
SET hive.exec.parallel = true;
SET hive.exec.parallel.thread.number = 8;

-- 本地模式(数据量小时在本地执行,无需提交集群)
SET hive.exec.mode.local.auto = true;

-- 向量化查询(批量处理,提升 CPU 效率)
SET hive.vectorized.execution.enabled = true;

-- CBO 优化器(基于代价的优化)
SET hive.cbo.enable = true;
SET hive.compute.query.using.stats = true;

本站内容由 褚成志 整理编写,仅供学习参考