Skip to content

第 6 章 广电用户收视行为数据查询优化 ​

开篇自学说明 ​

本章是 Hive 从「能写对 SQL」到「能写好 SQL」的核心跨越,解决的核心痛点是:面对百万级 / 千万级的广电用户收视行为数据,之前的基础查询会出现执行慢、耗资源、甚至任务卡死的问题。

本章所有内容均围绕广电核心业务表media_index(用户收视行为表,超 100 万条数据)展开,学完本章你将掌握 3 大核心能力:

  1. 用视图简化复杂查询,降低 SQL 维护成本,提升复用性
  2. 通过 Hive 配置优化,让简单查询从几十秒提速到毫秒级,聚合查询效率提升 30% 以上
  3. 通过 SQL 语句优化,解决大数据量下的数据倾斜、任务卡死、全表扫描等核心问题

前置必备准备

  1. 已完成第 3 章media_index用户收视行为表的创建与数据导入
  2. 已掌握第 4、5 章的基础查询、函数、JOIN、分组聚合等核心语法
  3. 进入 Hive 客户端后,先执行以下基础配置(固定格式,提升查询体验)
hive
-- 切换到广电业务库
USE ZJSM;
-- 开启查询结果列名显示
SET hive.cli.print.header = true;
-- 列名仅显示字段名,不显示表名前缀
SET hive.resultset.use.unique.column.names=false;
-- 提示符显示当前所在数据库,避免走错库
SET hive.cli.print.current.db=true;

核心业务表字段回顾(本章所有案例均基于此表)

media_index广电用户收视行为表,核心字段如下:

字段名业务含义核心用途
phone_no用户编号(唯一标识)关联用户、统计观看用户数
station_name直播频道名称统计频道热度、直播频道数量
res_type节目类型0 = 直播,1 = 点播 / 回看,区分观看类型
origin_time观看行为开始时间计算观看时段、观看时长
end_time观看行为结束时间计算观看周期、观看时长
duration观看时长(毫秒)统计用户观看粘性、节目热度
owner_name用户等级名称分用户等级统计观看行为
program_title直播节目名称统计单节目观看人数
vod_title点播节目名称统计点播节目热度
region节目地区信息分地域统计观看偏好

模块一:Hive 视图(降低查询复杂度的核心工具) ​

一、大白话理解视图 ​

视图是基于 Hive 基本表创建的伪表 / 虚拟表,数据库只存储视图的定义(SQL 查询逻辑),不存储真实数据,数据依然存在原始的基本表中。

你可以把视图理解成「常用 SQL 查询的快捷方式」,把复杂的、高频使用的查询逻辑封装成视图,后续直接查询视图即可,不用每次都重复写一大段 SQL。

二、视图的核心作用(新手必懂) ​

  1. 简化复杂 SQL:把多表关联、多层子查询的复杂逻辑封装成视图,查询时直接SELECT * FROM 视图名,大幅降低 SQL 书写难度
  2. 提升代码复用性:高频使用的统计逻辑封装成视图,多人协作、多次查询时直接调用,不用重复开发
  3. 数据安全控制:可以只给下游用户开放视图的查询权限,不开放原始表的权限,屏蔽敏感字段、过滤敏感数据
  4. 屏蔽底层表结构变化:如果原始表字段有调整,只需要修改视图的定义,不用修改下游的查询代码

三、视图的核心操作(语法 + 广电业务案例) ​

1. 创建视图 ​

核心语法

hive
CREATE VIEW [IF NOT EXISTS] [库名.]视图名 
  [(列名1 [COMMENT 列注释], 列名2 [COMMENT 列注释], ...) ] 
  [COMMENT 视图注释]
  [TBLPROPERTIES (属性名=属性值, ...)] 
AS SELECT 查询语句;
  • IF NOT EXISTS:必加,避免视图已存在时报错
  • 列名可以自定义,数量和类型必须和后面 SELECT 语句的结果完全一致;不写则默认继承 SELECT 语句的列名
  • AS SELECT:视图的核心,定义视图的查询逻辑

入门案例 1:创建收视时间视图(任务 6.1 配套)

hive
-- 代码6-1 创建media_index_time_view视图,封装用户观看时间核心字段
CREATE VIEW IF NOT EXISTS media_index_time_view (
  phone_no COMMENT '用户编号',
  station_name COMMENT '直播频道名称',
  origin_time COMMENT '观看开始时间',
  end_time COMMENT '观看结束时间'
)
COMMENT '用户收视时间视图'
AS SELECT phone_no, station_name, origin_time, end_time 
FROM media_index;

-- 查询视图,和查询普通表语法完全一致
SELECT * FROM media_index_time_view LIMIT 10;

入门案例 2:创建节目类型视图(任务 6.1 核心)

hive
-- 代码6-3 创建media_index_type_view视图,封装节目类型核心字段
CREATE VIEW IF NOT EXISTS media_index_type_view (
  phone_no COMMENT '用户编号',
  res_type COMMENT '节目类型(0=直播,1=点播/回看)'
)
COMMENT '用户节目类型视图'
AS SELECT phone_no, res_type 
FROM media_index;

2. 查看视图 ​

hive
-- 1. 查看当前库下所有表和视图(Hive所有版本通用)
SHOW TABLES;

-- 2. Hive 2.2.0+版本专属,仅查看所有视图
SHOW VIEWS;

-- 3. 查看视图的表结构、字段信息
DESC media_index_time_view;
-- 查看视图的详细定义、创建语句
DESC FORMATTED media_index_time_view;

3. 删除视图 ​

hive
-- 核心语法,仅删除视图的定义,不会影响原始基本表的数据
DROP VIEW [IF EXISTS] 视图名;

-- 示例:删除收视时间视图
DROP VIEW IF EXISTS media_index_time_view;

✅ 新手避坑:绝对不能用DROP TABLE删除视图,会执行失败;视图只能用DROP VIEW删除。

4. 修改视图 ​

hive
-- 修改视图的查询逻辑,语法和创建视图一致,用ALTER VIEW
ALTER VIEW 视图名
AS SELECT 新的查询语句;

-- 示例:给节目类型视图增加用户等级字段
ALTER VIEW media_index_type_view
AS SELECT phone_no, res_type, owner_name
FROM media_index;

四、核心业务实战(任务 6.1:使用视图统计不同节目的用户观看人数) ​

需求:统计直播、点播 / 回看两种节目类型的观看用户数,对比哪种观看方式更受欢迎。

hive
-- 步骤1:创建节目类型视图,封装核心字段
CREATE VIEW IF NOT EXISTS media_index_type_view (
  phone_no,
  res_type
)
COMMENT '节目类型统计专用视图'
AS SELECT phone_no, res_type 
FROM media_index;

-- 步骤2:使用视图统计不同节目类型的去重用户数
SELECT 
  res_type AS `节目类型`,
  COUNT(DISTINCT phone_no) AS `观看用户数`
FROM media_index_type_view
GROUP BY res_type;

执行结果说明:

  • res_type=0(直播):观看用户数 6333 人
  • res_type=1(点播 / 回看):观看用户数 2448 人
  • 结论:观看直播的用户数是点播 / 回看的 2.6 倍

五. 视图的限制(避坑指南) ​

  1. 只读性:视图是只读的。你不能对视图执行 INSERT、UPDATE 或 DELETE。它仅仅是底层表的一个逻辑映射。
  2. 不存储数据:视图不占用磁盘空间(只在元数据库 MySQL 中占用极小的记录空间)。
  3. 性能开销:由于视图是“实时执行”的,每次查询视图都会重新跑一遍底层的逻辑。如果视图定义非常复杂,查询可能会很慢。
    • 注:如果你需要物理存储结果以提升性能,应该使用 物化视图 (Materialized View) 或直接 CREATE TABLE AS SELECT。
  4. 元数据依赖:如果视图依赖的底层表被删除了(Drop),视图虽然还在,但查询时会报错。

六. 什么时候该用视图? ​

  • 场景 1:你有一张宽表,但你每天只关心其中的 5 个字段。
  • 场景 2:你需要把生产环境的表(明细层 DWD)转换成业务人员容易理解的报表层(应用层 APP)。
  • 场景 3:在进行数据库迁移或重构时,通过视图可以保持接口名不变,底层指向新表,实现平滑过渡。

七、视图使用的核心避坑点 ​

  1. 视图不存储数据,所有查询都依赖原始表

    • 坑:删除了原始基本表,再查询视图会直接报错
    • 解决:视图必须依赖存在的基本表,删除基表前先删除关联视图
  2. 基本表的结构变更不会自动同步到视图

    • 坑:给原始表新增了字段,视图里不会自动出现;删除了视图用到的字段,视图查询会报错
    • 解决:基表结构变更后,必须用ALTER VIEW手动更新视图的定义
  3. 视图不能插入、修改数据

    • 坑:尝试用 INSERT/UPDATE 修改视图数据,直接报错
    • 解决:视图仅支持查询操作,数据修改必须在原始基本表中执行
  4. 视图嵌套过深会导致性能下降

    • 坑:视图里嵌套视图,超过 3 层后,查询性能会大幅下降
    • 解决:尽量控制视图的嵌套层数,最多不超过 2 层,复杂逻辑直接写在视图定义里

模块二:Hive 配置优化(改个配置就能提速,新手必学) ​

很多时候查询慢,不是 SQL 写的有问题,而是 Hive 的默认配置没有针对大数据场景做优化。本模块的配置优化,能让你的查询效率有质的飞跃,所有配置均为会话级生效,退出 Hive 客户端后重新登录需要重新执行。

一、Fetch 抓取优化(简单查询毫秒级提速) ​

1. 大白话理解 ​

Hive 默认对简单查询(比如SELECT * FROM 表 LIMIT 5)也会启动 MapReduce 任务执行,而 MapReduce 的启动、初始化时间远大于数据查询时间,导致一个简单查询要几十秒才能出结果。

Fetch 抓取就是让 Hive 对特定场景的查询,不启动 MapReduce,直接读取 HDFS 上的文件返回结果,查询时间从几十秒降到零点几秒。

2. 核心配置 ​

hive
-- 核心配置,开启全量Fetch抓取(Hive 3.1.2+默认是more)
SET hive.fetch.task.conversion = more;

-- 关闭Fetch抓取,所有查询都走MapReduce(仅测试对比用,生产禁止)
SET hive.fetch.task.conversion = none;

参数值说明

参数值生效场景
none所有查询都必须走 MapReduce,性能极差
minimal仅全局查找、分区字段查询、LIMIT 查询不走 MapReduce
more全局查询、字段查询、LIMIT 查询、简单过滤查询都不走 MapReduce,覆盖场景最全,新手必设

3. 效果对比 ​

  • 配置none时,SELECT * FROM media_index LIMIT 5; 执行时间 72.547 秒,需要启动完整的 MapReduce 任务
  • 配置more时,同一条 SQL 执行时间 0.381 秒,直接读取文件返回结果,无 MapReduce 开销

二、合理设置 Map 和 Reduce 任务数(聚合查询提速核心) ​

Hive 的查询最终会转化为 MapReduce 任务执行,Map 负责读取和过滤数据,Reduce 负责聚合和排序。任务数设置不合理,会直接导致查询变慢:

  • Map 任务太少:每个 Map 处理的数据量太大,单任务执行慢
  • Map 任务太多:任务启动和初始化的开销超过数据处理开销,资源浪费
  • Reduce 任务太少:单个 Reduce 处理的数据量太大,甚至出现数据倾斜,任务卡死
  • Reduce 任务太多:产生大量小文件,给 HDFS 造成压力

1. Map 任务数优化 ​

(1)核心原理 ​

Map 任务的数量默认由输入文件的数量、HDFS 块大小(默认 128MB/256MB)决定。

  • 小文件过多:每个小文件都会启动一个 Map 任务,造成资源浪费
  • 大文件过多:单个 Map 处理的数据量太大,执行慢
(2)优化配置 ​
hive
-- 1. 开启Map执行前合并小文件,减少Map任务数(解决小文件过多问题)
SET hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

-- 2. 调整Map切片大小,控制Map任务数量
-- 场景1:文件太大,Map任务太少,调小切片大小,增加Map任务数
-- 示例:设置最大切片大小为128MB(134217728字节),默认256MB
SET mapreduce.input.fileinputformat.split.maxsize=134217728;

-- 场景2:小文件太多,Map任务太多,调大切片大小,减少Map任务数
SET mapreduce.input.fileinputformat.split.maxsize=268435456; -- 256MB
(3)效果对比 ​
  • 默认切片 256MB 时,780MB 的收视行为表统计总行数,启动 4 个 Map 任务,执行时间 62.273 秒
  • 切片调整为 128MB 后,启动 7 个 Map 任务,执行时间 52.472 秒,提速 15%+

2. Reduce 任务数优化 ​

(1)核心原理 ​

Reduce 任务的数量默认由 Hive 内置算法决定,默认每个 Reduce 处理 256MB 数据,最大 Reduce 数 1009 个。

  • 核心公式:合理的 Reduce 数 = 输入数据总大小 ÷ 每个 Reduce 处理的数据量
  • 示例:780MB 的表,每个 Reduce 处理 256MB,合理的 Reduce 数是 4 个
(2)优化配置 ​
hive
-- 1. 查看默认配置
-- 查看每个Reduce默认处理的数据量(默认256MB)
SET hive.exec.reducers.bytes.per.reducer;
-- 查看最大允许的Reduce任务数
SET hive.exec.reducers.max;

-- 2. 手动设置Reduce任务数(最直接,新手推荐)
SET mapreduce.job.reduces=4;

-- 3. 调整每个Reduce处理的数据量,间接控制Reduce数
-- 示例:设置每个Reduce处理128MB数据,增加Reduce数
SET hive.exec.reducers.bytes.per.reducer=134217728;

✅ 新手避坑:Reduce 任务数不是越多越好,必须和数据量匹配,过多会产生大量小文件,过少会导致单 Reduce 压力过大。

三、并行执行配置(复杂查询提速) ​

1. 大白话理解 ​

复杂的 Hive SQL 会被拆分成多个执行阶段(Stage),默认 Hive 一次只执行一个阶段。但很多阶段之间没有依赖关系,可以并行执行,大幅缩短整体查询时间。

2. 核心配置 ​

hive
-- 1. 开启任务并行执行(默认关闭,必须手动开启)
SET hive.exec.parallel=true;

-- 2. 设置最大并行度,默认8,根据集群资源调整,新手用默认8即可
SET hive.exec.parallel.thread.number=8;

✅ 注意:并行执行会占用更多的集群资源,仅在多阶段的复杂查询中生效,简单查询无需开启。


模块三:HQL 语句优化(从「写对」到「写好」的核心) ​

配置优化是「硬件提速」,而 SQL 语句优化是「软件提速」,能从根本上解决大数据量下的查询慢、数据倾斜、任务卡死问题。

一、子查询优化:先过滤,后关联 / 聚合 ​

1. 核心原理 ​

新手最容易犯的错误:先做全表 JOIN / 全表聚合,再用 WHERE 过滤数据。这种写法会先扫描全表所有数据,再过滤,数据量越大,性能越差。

正确的优化逻辑:先通过 WHERE 子查询过滤掉无用数据,缩小数据量,再做 JOIN / 聚合,大幅减少需要处理的数据量。

2. 错误写法 vs 正确写法(广电业务案例) ​

需求:查询用户数小于 500 的用户等级名称

hive
-- ❌ 错误写法:先全表分组聚合,再过滤
SELECT owner_name, COUNT(phone_no) AS user_count
FROM media_index
GROUP BY owner_name
HAVING COUNT(phone_no) < 500;

-- ✅ 正确写法:子查询先聚合,再过滤,性能更优
SELECT a.owner_name
FROM (
  -- 子查询:先分组统计每个等级的用户数
  SELECT owner_name, COUNT(phone_no) AS user_count 
  FROM media_index 
  GROUP BY owner_name
) a 
WHERE a.user_count < 500;

3. 进阶优化:JOIN 查询的子查询优化 ​

需求:统计正常状态用户的直播频道观看数据

hive
-- ❌ 错误写法:先全表JOIN,再过滤
SELECT u.phone_no, u.run_name, m.station_name, m.duration
FROM media_index m
LEFT JOIN mediamatch_usermsg u
ON m.phone_no = u.phone_no
WHERE m.res_type = 0 AND u.run_name = '正常';

-- ✅ 正确写法:子查询先过滤,再JOIN,大幅减少关联的数据量
SELECT u.phone_no, u.run_name, m.station_name, m.duration
FROM (
  -- 子查询1:先过滤出直播数据
  SELECT phone_no, station_name, duration
  FROM media_index
  WHERE res_type = 0
) m
LEFT JOIN (
  -- 子查询2:先过滤出正常状态的用户
  SELECT phone_no, run_name
  FROM mediamatch_usermsg
  WHERE run_name = '正常'
) u
ON m.phone_no = u.phone_no;

二、GROUP BY 语句优化(解决数据倾斜核心) ​

1. 核心原理 ​

GROUP BY 分组时,Map 阶段会把相同 key 的数据发送给同一个 Reduce 处理。如果某个 key 的数据量特别大(比如某个热门频道的观看记录有几十万条),就会导致这个 Reduce 处理的数据量远大于其他 Reduce,出现数据倾斜,整个任务卡在这个 Reduce 上无法完成。

优化核心:Map 端预聚合 + 数据倾斜负载均衡,把压力分散开。

2. 核心优化配置 ​

hive
-- 1. 开启Map端预聚合(默认开启,建议手动确认)
-- 作用:Map端先做局部聚合,减少发送到Reduce的数据量,大幅降低Shuffle开销
SET hive.map.aggr = true;

-- 2. 设置Map端预聚合的触发条数,默认10万,可根据数据量调整
SET hive.groupby.mapaggr.checkinterval = 10000000;

-- 3. 开启数据倾斜负载均衡(核心,解决分组倾斜,默认关闭)
-- 作用:开启后会启动两个MapReduce任务,第一个任务随机分发key,分散压力,第二个任务做最终聚合
SET hive.groupby.skewindata = true;

3. 效果对比 ​

  • 未开启优化时,分组统计用户等级名称,执行时间 105.8 秒
  • 开启优化后,同一条 SQL 执行时间 82.268 秒,提速 22%+,解决了数据倾斜导致的任务卡顿

三、COUNT (DISTINCT) 去重优化(大数据量必改) ​

1. 核心原理 ​

COUNT(DISTINCT 字段)会把所有去重的工作交给一个 Reduce来处理,即使你设置了多个 Reduce 也不会生效。当数据量达到百万级以上时,这个 Reduce 会处理海量数据,直接导致任务卡死。

优化方案:先用 GROUP BY 分组去重,再用 COUNT 统计,把去重工作分散到多个 Reduce 中,避免单 Reduce 压力过大。

2. 错误写法 vs 正确写法 ​

需求:统计收视行为表中的去重用户总数

hive
-- ❌ 错误写法:COUNT(DISTINCT),单Reduce处理,大数据量卡死
SELECT COUNT(DISTINCT phone_no) AS user_total
FROM media_index;
-- 执行时间:51.115秒,仅1个Reduce任务

-- ✅ 正确写法:先GROUP BY分组去重,再COUNT统计,多Reduce分散压力
SELECT COUNT(phone_no) AS user_total
FROM (
  -- 子查询:先按用户编号分组,实现去重
  SELECT phone_no 
  FROM media_index 
  GROUP BY phone_no
) a;
-- 执行时间:77.993秒,启动4个Reduce任务,任务更稳定,不会卡死

✅ 新手说明:小数据量下,COUNT (DISTINCT) 写法更简洁,性能差异不大;但百万级以上大数据量下,必须用 GROUP BY 替代,保证任务能稳定执行,不会出现单 Reduce 卡死的问题。

四、LIMIT 语句优化(避免全表扫描) ​

1. 核心原理 ​

默认情况下,LIMIT n语句会先执行全表查询,再返回前 n 条数据,即使只需要 10 条数据,也会扫描全表,大数据量下性能极差。

开启 LIMIT 优化后,Hive 会对数据做抽样查询,直接返回抽样后的 n 条数据,不用扫描全表,大幅提升查询速度。

2. 核心优化配置 ​

hive
-- 1. 开启LIMIT语句优化(默认关闭)
SET hive.limit.optimize.enable=true;

-- 2. 设置最小采样容量,默认10万,可根据数据量调整
SET hive.limit.row.max.size=100000;

-- 3. 设置可抽样的最大文件数,默认10
SET hive.limit.optimize.limit.file=10;

-- 4. 安全配置:强制ORDER BY必须配合LIMIT使用,避免全表排序导致的单Reduce卡死
SET hive.strict.checks.orderby.no.limit = true;

模块四:三大核心任务完整实战(全流程可复制) ​

任务 6.1 使用视图统计不同节目的用户观看人数 ​

完整执行步骤,可直接复制到 Hive 客户端执行:

hive
-- 步骤1:切换到业务库
USE ZJSM;

-- 步骤2:创建节目类型视图,封装核心字段
CREATE VIEW IF NOT EXISTS media_index_type_view (
  phone_no COMMENT '用户编号',
  res_type COMMENT '节目类型'
)
COMMENT '统计节目类型用户数专用视图'
AS SELECT phone_no, res_type FROM media_index;

-- 步骤3:统计不同节目类型的去重用户数
SELECT 
  res_type AS `节目类型`,
  COUNT(DISTINCT phone_no) AS `观看用户数`
FROM media_index_type_view
GROUP BY res_type;

任务 6.2 优化统计直播频道数 ​

完整执行步骤,包含全量优化配置:

hive
-- 步骤1:创建频道视图,仅保留直播频道字段,减少数据扫描
CREATE VIEW IF NOT EXISTS media_index_station_view (
  station_name COMMENT '直播频道名称'
)
COMMENT '统计直播频道数专用视图'
AS SELECT station_name FROM media_index;

-- 步骤2:全量优化配置
-- 开启Fetch抓取
SET hive.fetch.task.conversion = more;
-- 合并小文件,调整切片大小为128MB,增加Map任务数
SET hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
SET mapreduce.input.fileinputformat.split.maxsize=134217728;
-- 开启任务并行执行
SET hive.exec.parallel=true;
-- 开启Map端预聚合
SET hive.map.aggr = true;

-- 步骤3:去重统计直播频道总数
SELECT COUNT(DISTINCT station_name) AS `直播频道总数`
FROM media_index_station_view;

执行结果:直播频道总数 147 个,优化后执行时间 50.399 秒

任务 6.3 使用子查询统计节目类型为直播的频道 Top10 ​

完整执行步骤,全量优化配置 + 子查询优化:

hive
-- 步骤1:创建Top10统计专用视图,过滤无关字段
CREATE VIEW IF NOT EXISTS media_index_Top10_view (
  phone_no,
  station_name,
  res_type
)
COMMENT '直播频道Top10统计专用视图'
AS SELECT phone_no, station_name, res_type FROM media_index;

-- 步骤2:全量优化配置
-- 调整切片大小,增加Map任务数
SET mapreduce.input.fileinputformat.split.maxsize=134217728;
-- 设置合理的Reduce任务数
SET mapreduce.job.reduces=4;
-- Map端预聚合配置
SET hive.map.aggr = true;
SET hive.groupby.mapaggr.checkinterval = 10000000;
-- 开启数据倾斜负载均衡
SET hive.groupby.skewindata = true;
-- 开启并行执行
SET hive.exec.parallel=true;
-- 强制ORDER BY必须加LIMIT
SET hive.strict.checks.orderby.no.limit = true;

-- 步骤3:子查询优化,统计直播频道观看用户数Top10
SELECT 
  a.station_name AS `直播频道名称`,
  COUNT(a.phone_no) AS `观看用户数`
FROM (
  -- 子查询:先过滤出直播类型的数据,缩小数据量
  SELECT * 
  FROM media_index_Top10_view 
  WHERE res_type = '0'
) a
-- 按频道名称分组
GROUP BY a.station_name
-- 按观看用户数降序排列
ORDER BY `观看用户数` DESC
-- 取Top10
LIMIT 10;

执行结果:返回观看用户数最高的 10 个频道,比如中央 1 台 - 高清、中央 5 台 - 高清、翡翠台等,优化后执行时间 136.519 秒


模块五:新手高频踩坑避坑指南 ​

  1. 视图查询报错「表不存在」

    • 坑:删除了视图依赖的原始基本表,再查询视图直接报错
    • 解决:视图仅存储查询逻辑,不存储数据,必须保证依赖的基表存在;删除基表前先删除关联视图
  2. 优化配置不生效

    • 坑:配置了优化参数,下次登录 Hive 又恢复默认了
    • 解决:本章所有配置都是会话级生效,仅当前 Hive 客户端连接有效;想永久生效,需要修改hive-site.xml配置文件
  3. 子查询执行报错

    • 坑:子查询没有加别名,执行报错Table not found
    • 解决:FROM 后面的子查询,必须给子查询结果加一个表别名,否则 Hive 无法识别
  4. GROUP BY 任务长时间卡住不动

    • 坑:分组任务卡在 reduce 100% 不动,是典型的数据倾斜
    • 解决:开启SET hive.groupby.skewindata = true;,用子查询先过滤小数据量,再分组
  5. COUNT (DISTINCT) 任务卡死

    • 坑:百万级数据用 COUNT (DISTINCT) 去重,任务一直卡在 map 100%,reduce 0%
    • 解决:用「子查询 GROUP BY 分组去重 + 外层 COUNT 统计」替代,分散 Reduce 压力
  6. ORDER BY 查询执行极慢

    • 坑:全表排序不加 LIMIT,所有数据进入一个 Reduce 做全局排序,任务执行极慢
    • 解决:ORDER BY 必须配合 LIMIT 使用,开启严格模式SET hive.strict.checks.orderby.no.limit = true;强制限制
  7. 视图嵌套过深导致性能下降

    • 坑:视图里嵌套视图,超过 3 层,查询性能大幅下降
    • 解决:视图嵌套最多不超过 2 层,复杂逻辑直接写在视图定义里,不要多层嵌套

模块六:入门自测题(学完检验成果) ​

题目 1 ​

请创建一个视图user_view_duration_view,包含用户编号、观看时长、节目类型、频道名称,用于统计用户观看时长,要求给字段加中文注释。

参考答案
hive
CREATE VIEW IF NOT EXISTS user_view_duration_view (
  phone_no COMMENT '用户编号',
  duration COMMENT '观看时长(毫秒)',
  res_type COMMENT '节目类型',
  station_name COMMENT '频道/节目名称'
)
COMMENT '用户观看时长统计专用视图'
AS SELECT phone_no, duration, res_type, station_name
FROM media_index;

题目 2 ​

请写出优化后的 SQL,统计收视行为表中,每个直播频道的总观看时长,要求包含本章学到的配置优化和语句优化。

参考答案
hive
-- 优化配置
SET mapreduce.input.fileinputformat.split.maxsize=134217728;
SET mapreduce.job.reduces=4;
SET hive.map.aggr = true;
SET hive.groupby.skewindata = true;
SET hive.exec.parallel=true;

-- 优化后的SQL,先过滤再聚合
SELECT 
  station_name AS `直播频道名称`,
  SUM(CAST(duration AS BIGINT)) AS `总观看时长(毫秒)`
FROM (
  SELECT station_name, duration
  FROM media_index
  WHERE res_type = '0' AND duration IS NOT NULL
) a
GROUP BY station_name
ORDER BY `总观看时长(毫秒)` DESC;

题目 3 ​

请写出 SQL,用优化后的方式统计收视行为表中,有过观看行为的去重用户总数,避免单 Reduce 压力过大。

参考答案
hive
-- 优化配置
SET mapreduce.job.reduces=4;

-- 先GROUP BY去重,再COUNT统计
SELECT COUNT(phone_no) AS `观看用户总数`
FROM (
  SELECT phone_no
  FROM media_index
  WHERE phone_no IS NOT NULL
  GROUP BY phone_no
) t;

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