第 6 章 广电用户收视行为数据查询优化
开篇自学说明
本章是 Hive 从「能写对 SQL」到「能写好 SQL」的核心跨越,解决的核心痛点是:面对百万级 / 千万级的广电用户收视行为数据,之前的基础查询会出现执行慢、耗资源、甚至任务卡死的问题。
本章所有内容均围绕广电核心业务表media_index(用户收视行为表,超 100 万条数据)展开,学完本章你将掌握 3 大核心能力:
- 用视图简化复杂查询,降低 SQL 维护成本,提升复用性
- 通过 Hive 配置优化,让简单查询从几十秒提速到毫秒级,聚合查询效率提升 30% 以上
- 通过 SQL 语句优化,解决大数据量下的数据倾斜、任务卡死、全表扫描等核心问题
前置必备准备
- 已完成第 3 章
media_index用户收视行为表的创建与数据导入 - 已掌握第 4、5 章的基础查询、函数、JOIN、分组聚合等核心语法
- 进入 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。
二、视图的核心作用(新手必懂)
- 简化复杂 SQL:把多表关联、多层子查询的复杂逻辑封装成视图,查询时直接
SELECT * FROM 视图名,大幅降低 SQL 书写难度 - 提升代码复用性:高频使用的统计逻辑封装成视图,多人协作、多次查询时直接调用,不用重复开发
- 数据安全控制:可以只给下游用户开放视图的查询权限,不开放原始表的权限,屏蔽敏感字段、过滤敏感数据
- 屏蔽底层表结构变化:如果原始表字段有调整,只需要修改视图的定义,不用修改下游的查询代码
三、视图的核心操作(语法 + 广电业务案例)
1. 创建视图
核心语法
CREATE VIEW [IF NOT EXISTS] [库名.]视图名
[(列名1 [COMMENT 列注释], 列名2 [COMMENT 列注释], ...) ]
[COMMENT 视图注释]
[TBLPROPERTIES (属性名=属性值, ...)]
AS SELECT 查询语句;IF NOT EXISTS:必加,避免视图已存在时报错- 列名可以自定义,数量和类型必须和后面 SELECT 语句的结果完全一致;不写则默认继承 SELECT 语句的列名
AS SELECT:视图的核心,定义视图的查询逻辑
入门案例 1:创建收视时间视图(任务 6.1 配套)
-- 代码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 核心)
-- 代码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. 查看视图
-- 1. 查看当前库下所有表和视图(Hive所有版本通用)
SHOW TABLES;
-- 2. Hive 2.2.0+版本专属,仅查看所有视图
SHOW VIEWS;
-- 3. 查看视图的表结构、字段信息
DESC media_index_time_view;
-- 查看视图的详细定义、创建语句
DESC FORMATTED media_index_time_view;3. 删除视图
-- 核心语法,仅删除视图的定义,不会影响原始基本表的数据
DROP VIEW [IF EXISTS] 视图名;
-- 示例:删除收视时间视图
DROP VIEW IF EXISTS media_index_time_view;✅ 新手避坑:绝对不能用DROP TABLE删除视图,会执行失败;视图只能用DROP VIEW删除。
4. 修改视图
-- 修改视图的查询逻辑,语法和创建视图一致,用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:使用视图统计不同节目的用户观看人数)
需求:统计直播、点播 / 回看两种节目类型的观看用户数,对比哪种观看方式更受欢迎。
-- 步骤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 倍
五. 视图的限制(避坑指南)
- 只读性:视图是只读的。你不能对视图执行
INSERT、UPDATE或DELETE。它仅仅是底层表的一个逻辑映射。 - 不存储数据:视图不占用磁盘空间(只在元数据库 MySQL 中占用极小的记录空间)。
- 性能开销:由于视图是“实时执行”的,每次查询视图都会重新跑一遍底层的逻辑。如果视图定义非常复杂,查询可能会很慢。
- 注:如果你需要物理存储结果以提升性能,应该使用 物化视图 (Materialized View) 或直接
CREATE TABLE AS SELECT。
- 注:如果你需要物理存储结果以提升性能,应该使用 物化视图 (Materialized View) 或直接
- 元数据依赖:如果视图依赖的底层表被删除了(Drop),视图虽然还在,但查询时会报错。
六. 什么时候该用视图?
- 场景 1:你有一张宽表,但你每天只关心其中的 5 个字段。
- 场景 2:你需要把生产环境的表(明细层 DWD)转换成业务人员容易理解的报表层(应用层 APP)。
- 场景 3:在进行数据库迁移或重构时,通过视图可以保持接口名不变,底层指向新表,实现平滑过渡。
七、视图使用的核心避坑点
视图不存储数据,所有查询都依赖原始表
- 坑:删除了原始基本表,再查询视图会直接报错
- 解决:视图必须依赖存在的基本表,删除基表前先删除关联视图
基本表的结构变更不会自动同步到视图
- 坑:给原始表新增了字段,视图里不会自动出现;删除了视图用到的字段,视图查询会报错
- 解决:基表结构变更后,必须用
ALTER VIEW手动更新视图的定义
视图不能插入、修改数据
- 坑:尝试用 INSERT/UPDATE 修改视图数据,直接报错
- 解决:视图仅支持查询操作,数据修改必须在原始基本表中执行
视图嵌套过深会导致性能下降
- 坑:视图里嵌套视图,超过 3 层后,查询性能会大幅下降
- 解决:尽量控制视图的嵌套层数,最多不超过 2 层,复杂逻辑直接写在视图定义里
模块二:Hive 配置优化(改个配置就能提速,新手必学)
很多时候查询慢,不是 SQL 写的有问题,而是 Hive 的默认配置没有针对大数据场景做优化。本模块的配置优化,能让你的查询效率有质的飞跃,所有配置均为会话级生效,退出 Hive 客户端后重新登录需要重新执行。
一、Fetch 抓取优化(简单查询毫秒级提速)
1. 大白话理解
Hive 默认对简单查询(比如SELECT * FROM 表 LIMIT 5)也会启动 MapReduce 任务执行,而 MapReduce 的启动、初始化时间远大于数据查询时间,导致一个简单查询要几十秒才能出结果。
Fetch 抓取就是让 Hive 对特定场景的查询,不启动 MapReduce,直接读取 HDFS 上的文件返回结果,查询时间从几十秒降到零点几秒。
2. 核心配置
-- 核心配置,开启全量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)优化配置
-- 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)优化配置
-- 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. 核心配置
-- 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 的用户等级名称
-- ❌ 错误写法:先全表分组聚合,再过滤
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 查询的子查询优化
需求:统计正常状态用户的直播频道观看数据
-- ❌ 错误写法:先全表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. 核心优化配置
-- 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 正确写法
需求:统计收视行为表中的去重用户总数
-- ❌ 错误写法: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. 核心优化配置
-- 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 客户端执行:
-- 步骤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 优化统计直播频道数
完整执行步骤,包含全量优化配置:
-- 步骤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
完整执行步骤,全量优化配置 + 子查询优化:
-- 步骤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 秒
模块五:新手高频踩坑避坑指南
视图查询报错「表不存在」
- 坑:删除了视图依赖的原始基本表,再查询视图直接报错
- 解决:视图仅存储查询逻辑,不存储数据,必须保证依赖的基表存在;删除基表前先删除关联视图
优化配置不生效
- 坑:配置了优化参数,下次登录 Hive 又恢复默认了
- 解决:本章所有配置都是会话级生效,仅当前 Hive 客户端连接有效;想永久生效,需要修改
hive-site.xml配置文件
子查询执行报错
- 坑:子查询没有加别名,执行报错
Table not found - 解决:FROM 后面的子查询,必须给子查询结果加一个表别名,否则 Hive 无法识别
- 坑:子查询没有加别名,执行报错
GROUP BY 任务长时间卡住不动
- 坑:分组任务卡在 reduce 100% 不动,是典型的数据倾斜
- 解决:开启
SET hive.groupby.skewindata = true;,用子查询先过滤小数据量,再分组
COUNT (DISTINCT) 任务卡死
- 坑:百万级数据用 COUNT (DISTINCT) 去重,任务一直卡在 map 100%,reduce 0%
- 解决:用「子查询 GROUP BY 分组去重 + 外层 COUNT 统计」替代,分散 Reduce 压力
ORDER BY 查询执行极慢
- 坑:全表排序不加 LIMIT,所有数据进入一个 Reduce 做全局排序,任务执行极慢
- 解决:ORDER BY 必须配合 LIMIT 使用,开启严格模式
SET hive.strict.checks.orderby.no.limit = true;强制限制
视图嵌套过深导致性能下降
- 坑:视图里嵌套视图,超过 3 层,查询性能大幅下降
- 解决:视图嵌套最多不超过 2 层,复杂逻辑直接写在视图定义里,不要多层嵌套
模块六:入门自测题(学完检验成果)
题目 1
请创建一个视图user_view_duration_view,包含用户编号、观看时长、节目类型、频道名称,用于统计用户观看时长,要求给字段加中文注释。
参考答案
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,统计收视行为表中,每个直播频道的总观看时长,要求包含本章学到的配置优化和语句优化。
参考答案
-- 优化配置
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 压力过大。
参考答案
-- 优化配置
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;