(课堂笔记)Hive 分区、分桶与数据倾斜

发布时间:2026/5/20 20:40:30

(课堂笔记)Hive 分区、分桶与数据倾斜 本文系统介绍了Hive的核心技术与优化策略。主要内容包括1分区与分桶技术详细讲解静态/动态分区实现方法和分桶原理2数据倾斜问题及解决方案涵盖JOIN、GROUPBY等场景的优化方法3Hive高级功能如存储格式选择、排序方式比较和自定义函数开发4性能优化体系从运维配置、模型设计到SQL编写的多维度优化建议5典型面试问题解析如大表关联处理方案和参数调优技巧。文章通过大量实例演示和命令速查表帮助读者掌握Hive的核心技术要点和实战优化方法。课堂笔记Hive 分区、分桶与数据倾斜一、Hive 分区1.1 什么是分区分区是将表的数据按照某个字段的值分成不同的目录存储。查询时可以只扫描特定分区大大提高查询效率。1.2 静态分区定义使用分区时必须指定分区名keyvalue这就是静态分区。示例场景三位任课老师的成绩文件科目文件名内容语文chinese101,zhangsan,100102,lisi,88103,wangwu,78数学math101,zhangsan,89102,lisi,56103,wangwu,60英语english101,zhangsan,77102,lisi,67103,wangwu,98操作步骤步骤1构造数据文件bashcd /home/hadoop/2608/test touch chinese math english # 依次编辑文件写入内容步骤2创建分区表sqlcreate table t_class( sno int, sname string, score int ) partitioned by (subject string) row format delimited fields terminated by ,;步骤3加载数据到指定分区sql-- 加载语文数据到 yuwen 分区 load data local inpath /home/hadoop/2608/test/chinese into table t_class partition (subjectyuwen); -- 加载数学数据到 shuxue 分区 load data local inpath /home/hadoop/2608/test/math into table t_class partition (subjectshuxue); -- 加载英语数据到 yingyu 分区 load data local inpath /home/hadoop/2608/test/english into table t_class partition (subjectyingyu);步骤4查询某个分区数据sqlselect * from t_class where subject yuwen;步骤5新增分区sqlalter table t_class add partition (subjecthistory);步骤6插入数据到分区sqlload data ... -- 方式一加载文件 -- 方式二从其他表插入 insert overwrite table t_class partition (subjecthistory) select sno, sname, score from t_class where subject yuwen;步骤7删除分区方式命令页面删除直接在 Web 界面操作HDFS 删除hadoop fs -rm -r 分区目录Hive 删除alter table t_class drop partition (subjectmath);1.3 静态分区练习练习要求构造三个月份的产品销售数据文件内容202001111,apple,7788112,huawei,8899113,xiaomi,6666202002111,apple,656112,huawei,77567113,xiaomi,86786202003111,apple,546112,huawei,768678113,xiaomi,867876练习答案1. 创建分区表sqlcreate table t_sales( pro_id int, pro_name string, quantity int ) partitioned by (month int) row format delimited fields terminated by ,;2. 加载数据到指定分区sqlload data local inpath /home/hadoop/2608/test/202001 into table t_sales partition (month202001); load data local inpath /home/hadoop/2608/test/202002 into table t_sales partition (month202002); load data local inpath /home/hadoop/2608/test/202003 into table t_sales partition (month202003);3. 添加分区sqlalter table t_sales add partition (month202004);4. 从现有分区插入数据到新分区sqlinsert overwrite table t_sales partition (month202004) select pro_id, pro_name, quantity 1000 from t_sales where month 202002;5. 删除分区sqlalter table t_sales drop partition (month202001);1.4 动态分区定义分区名未知时MR 自动读取数据读取分区列将数据写入对应的分区。核心特点特点说明自动分区MR 自动根据分区列的值创建分区使用简单可以把分区表当作普通表使用位置要求分区列必须在 SELECT 字段的最后面示例将 EMP 表数据写入分区表按 deptno 分区步骤1创建分区表sqlcreate table fenqu_emp( empno int, ename string, job string, mgr int, hiredate string, sal int, comm int ) partitioned by (deptno int) row format delimited fields terminated by \t;步骤2加载数据sql-- 方式一直接加载文件 load data local inpath /home/hadoop/2608/test/emp into table fenqu_emp; -- 方式二动态分区插入需要结果集提供分区值 insert into fenqu_emp select * from fenqu_emp where deptno is not null;注意动态分区时SELECT 的最后一个字段必须匹配分区列deptno1.5 动态分区练习练习要求将 dept 表的数据以 deptno 分区的方式存入 fenqu_dept。目标结果HDFS 的 fenqu_dept 目录下有 4 个子目录deptno10deptno20deptno30deptno40练习答案sql-- 1. 创建分区表 create table fenqu_dept( dname string, loc string ) partitioned by (deptno int) row format delimited fields terminated by \t; -- 2. 动态分区插入 insert into fenqu_dept select dname, loc, deptno from dept;二、Hive 分桶2.1 什么是分桶当分区数据分布不均匀时可以使用分桶将数据按照哈希值均匀分配到多个桶文件中。2.2 分桶原理text每条数据写入时MOD(哈希值, 桶数) → 决定放入哪个桶桶数每个桶数据量5 个桶1 亿条数据 → 每个桶约 2000 万条2.3 分表示例按 ENAME 分桶步骤1创建分桶表sqlcreate table fentong_emp( empno int, ename string, job string, mgr int, hiredate string, sal int, comm int, deptno int ) clustered by (ename) into 3 buckets row format delimited fields terminated by \t;步骤2写入数据sql-- 分桶表就是普通表直接插入 insert into fentong_emp select * from emp; -- ❌ 不推荐用 load data因为不会触发分桶 -- load data local inpath /home/hadoop/2608/test/emp into table fentong_emp;2.4 分桶练习练习要求创建分桶表 fentong_dept分桶键为 DNAME分 2 个桶并将 dept 表数据写入。练习答案sql-- 1. 创建分桶表 create table fentong_dept( deptno int, dname string, loc string ) clustered by (dname) into 2 buckets row format delimited fields terminated by \t; -- 2. 写入数据 insert into fentong_dept select * from dept;2.5 分区 vs 分桶对比对比项分区分桶存储方式分目录存放分文件存放分配规则有规律相同分区值放一起无规律哈希取模字段要求分区字段在数据文件中不存在分桶字段在数据文件中真实存在用途提高查询效率便于数据采样2.6 分桶的好处便于后期的数据采样使数据分布更均匀2.7 桶的个数定义规则text桶数 (总文件大小 / 128M) × 2说明128M 是 HDFS 的 Block Size 默认存储大小三、数据倾斜3.1 什么是数据倾斜Hive 将 SQL 转换成 MR 程序运行时Map 阶段生成的键值对分布不均导致某些节点处理的数据量非常大某些节点非常少。3.2 数据倾斜示例sqlSELECT 省份, SUM(金额) FROM 订单表 GROUP BY 省份;省份数据量问题广东10 亿条⚠️ 数据量大浙江2 亿条正常云南1000 万条正常内蒙1 万条正常总结Map 阶段数据分布不均导致单节点负载过高。3.3 数据倾斜的解决方案方案1JOIN 导致的数据倾斜问题场景sqlselect * from 订单表 m left join 用户表 n on m.客户ID n.客户ID解决方案方法说明Key 值打散case when m.客户ID is null then rand() else m.客户ID end n.客户IDMap Join把小表放到内存中驱动大表避免 shuffle 运算sql-- Map Join 写法 select /* mapjoin(n) */ * from 订单表 m left join 用户表 n on m.客户ID n.客户ID方案2GROUP BY 导致的数据倾斜解决方案方法说明缩小粒度GROUP BY 省份→GROUP BY 城市参数调优set hive.groupby.xxx Trueset hive.map.xxx True方案3小文件过多问题一个目录有几万甚至几十万个小文件解决方案方法说明合并小文件设置 combine 参数为 TrueInsert Overwrite用insert overwrite方式合并小文件方案4数据类型不一致问题场景sqlselect * from aaa left join bbb on aaa.id bbb.id; -- aaa.id 是 int (101) -- bbb.id 是 string (101)解决方案sql-- 用 CAST 统一数据类型 select * from aaa left join bbb on cast(aaa.id as string) bbb.id;3.4 面试题示例Q数据倾斜有碰到过吗原因是什么怎么解决的A之前有碰到过几次主要原因是因为 key 值分布不均导致的。具体案例1大表 JOIN 小表某些科目数据在大表里面体量非常大有的比较少导致 JOIN 时 Map 阶段分布不均解决方法使用 Map Join把小表放到内存驱动大表避免数据倾斜具体案例2小文件过多开发监控脚本监控 HDFS 上的分区目录文件个数某个分区表子目录文件个数超过阈值5000有几万个文件导致跑批任务时间非常久解决方法通过insert overwrite方式合并小文件解决数据倾斜四、命令速查表操作命令创建分区表create table ... partitioned by (col type)静态分区加载load data ... into table ... partition (colvalue)添加分区alter table ... add partition (colvalue)删除分区alter table ... drop partition (colvalue)创建分桶表create table ... clustered by (col) into n buckets插入分桶表insert into ... select ...不能用 load dataMap Joinselect /* mapjoin(小表) */ ...五、关键概念总结概念核心要点静态分区指定分区名手动加载动态分区自动识别分区分区列在 SELECT 最后分桶哈希取模均匀分布便于采样数据倾斜key 分布不均导致可用 Map Join、打散 key、合并小文件解决补充Hive 周边 面试常问问题本笔记针对初学者将课堂笔记中笼统的概念和理论进行详细解释帮助理解。一、Linux 环境执行 Hive 脚本1.1 为什么需要在 Linux 中执行 Hive 脚本在实际工作中我们通常不会手动在 Hive 窗口一条条输入 SQL而是将写好的 SQL 保存成文件通过 Linux 命令批量执行配合调度工具如 Azkaban定时运行1.2 方式一hive -ebashhive -e SQL语句作用切换到 Hive 窗口执行 SQL执行完后自动退出 Hive 窗口返回到 Linux 命令行。示例1直接执行查询bashhive -e select * from a2608.fenqu_emp注意如果之前没有用use 数据库名;切换数据库则表名需要带上数据库名格式为数据库名.表名。示例2多条 SQL 用分号隔开bashhive -e use mydb; show tables; select * from emp limit 10;1.3 方式二hive -fbashhive -f SQL文件路径作用将文件内容当作 Hive 命令执行执行完后自动退出。示例bash# 1. 创建一个 SQL 文件 echo select * from a2608.fenqu_emp; aaa.sql # 2. 执行这个文件 hive -f aaa.sql1.4 两种方式对比方式适用场景优点hive -eSQL 较短一次性执行简单快捷hive -fSQL 较长需要保存记录便于复用和版本管理二、Hive 的文件存储格式2.1 什么是存储格式存储格式决定了数据在 HDFS 上以什么方式组织和存放。2.2 常见存储格式格式类型特点TEXTFILE行式存储默认纯文本可读性好占用空间大ORC列式存储压缩比高查询快占用空间小SEQUENCEFILE行式存储二进制格式支持压缩2.3 行式存储 vs 列式存储行式存储如 TEXTFILEtext数据按行存放101,张三,90 → 102,李四,85 → 103,王五,88 查询时即使只需要姓名列也会读取整行数据列式存储如 ORCtext数据按列存放101,102,103 → 张三,李四,王五 → 90,85,88 查询时只需要读取需要的列大大减少 IO2.4 如何选择存储格式场景推荐格式原因数据量大TB/PB 级ORC压缩比高节省存储查询快列式存储数据量小几百 MBTEXTFILE简单直观便于排查问题需要与其他工具交互TEXTFILE通用性最好2.5 建表时指定存储格式sql-- 不指定时默认是 TEXTFILE create table aaa(id int) row format delimited fields terminated by , stored as orc; -- 指定 ORC 格式 -- 其他格式示例 stored as textfile; -- 文本格式默认 stored as sequencefile; -- 序列文件格式三、Hive 的特殊功能3.1 存储过程问题答案Hive 有存储过程吗没有为什么没有Hive 定位是数据仓库工具不是传统数据库不支持过程式编程3.2 自定义函数UDF什么是 UDF当 Hive 自带的函数无法满足业务需求时可以自己编写 Java 代码实现自定义函数。开发步骤编写 Java 类继承UDF类实现evaluate方法打包成 JAR 包在 Hive 中注册并使用sql-- 注册自定义函数 add jar /path/to/myudf.jar; create temporary function my_func as com.example.MyUDF; -- 使用自定义函数 select my_func(column) from table;3.3 索引问题答案Hive 有索引吗通常没有3.0 版本后有但不常用为什么不用索引分区和分桶已经提供了类似索引的查询优化效果四、Hive 的四种排序4.1 排序方式对比排序方式英文说明适用场景全局排序ORDER BY对所有数据排序只有一个 reducer数据量小时使用分区排序SORT BY每个 reducer 内部排序不保证全局有序数据量大只关心局部有序分发排序DISTRIBUTE BY按指定字段分发到不同 reducer需要将相同 key 的数据发到同一 reducer分区排序CLUSTER BY DISTRIBUTE BY SORT BY相同字段同时完成分发和排序4.2 详细解释ORDER BY全局排序sqlselect * from emp order by sal desc;特点所有数据进入一个 reducer保证全局有序缺点数据量大时非常慢注意需要设置set hive.mapred.modenonstrict;关闭严格模式SORT BY分区排序sqlselect * from emp sort by sal desc;特点每个 reducer内部排序但 reducer 之间无序举例如果有 3 个 reducer每个 reducer 输出的数据是排序的但 reducer1 的最后一个值可能大于 reducer2 的第一个值DISTRIBUTE BY分发sqlselect * from emp distribute by deptno;特点将相同 deptno 的数据分发到同一个 reducer用途为后续的排序或聚合做准备CLUSTER BY分区排序sqlselect * from emp cluster by deptno;等价于distribute by deptno sort by deptno特点按相同字段分发和排序4.3 图解排序方式textORDER BY: [所有数据] → reducer1 → [全局有序结果] SORT BY: [数据] → reducer1 → [有序子集1] [数据] → reducer2 → [有序子集2] [数据] → reducer3 → [有序子集3] 子集之间无序 DISTRIBUTE BY: [数据] → 按key分发 → reducer1 (所有key10) → reducer2 (所有key20) → reducer3 (所有key30) CLUSTER BY: [数据] → 按key分发 内部排序 → reducer1 (key10, 有序) → reducer2 (key20, 有序) → reducer3 (key30, 有序)五、HDFS 读写流程5.1 Hive 查询数据时底层 HDFS 是怎么工作的text┌─────────────────────────────────────────────────────────────┐ │ 查询流程 │ ├─────────────────────────────────────────────────────────────┤ │ 1. 客户端(Hive) 向 NameNode 发送读请求 │ │ 2. NameNode 验证权限检查文件是否存在 │ │ 3. NameNode 返回文件元数据文件位置、块信息 │ │ 4. 客户端根据元数据直接与 DataNode 通信读取数据 │ │ 5. DataNode 返回数据块内容 │ │ 6. Hive 组装数据返回给用户 │ └─────────────────────────────────────────────────────────────┘ 简化版流程Hive → NameNode获取元数据→ DataNode读取数据→ Hive5.2 写入流程插入数据text┌─────────────────────────────────────────────────────────────┐ │ 写入流程 │ ├─────────────────────────────────────────────────────────────┤ │ 1. 客户端向 NameNode 请求创建文件 │ │ 2. NameNode 检查权限和空间 │ │ 3. NameNode 返回可用的 DataNode 列表 │ │ 4. 客户端将数据分块写入 DataNode │ │ 5. DataNode 之间复制副本默认3份 │ │ 6. 写入完成NameNode 更新元数据 │ └─────────────────────────────────────────────────────────────┘六、Hive 优化面试重点6.1 优化可以从哪些角度入手角度负责角色主要内容运维角度运维工程师管理集群、服务器配置、节点扩展模型开发角度数据模型师表结构设计、分区/分桶、存储格式数据开发角度数据开发工程师SQL 写法、参数调优、代码规范6.2 运维角度优化优化项说明参数优化JVM 内存配置、动态分区严格模式、笛卡尔积严格模式节点扩展增加服务器节点提升计算能力硬件配套更好的 CPU、更大内存、SSD 硬盘6.3 模型开发角度优化优化项说明分区/分桶设计合理分区减少扫描量分桶便于采样列式存储数据量大时使用 ORC 格式压缩比高、查询快代理键设计使用org_id、account_id等整数类型作为关联键比字符串快6.4 数据开发角度优化重点优化项说明示例先过滤再关联减少 JOIN 的数据量SELECT * FROM a JOIN b ON a.idb.id WHERE a.date2024-01-01不如先过滤 a 表先过滤再分组减少 GROUP BY 的数据量先 WHERE 过滤再 GROUP BY慎用全局排序ORDER BY 只有一个 reducer数据量大时极慢能用 SORT BY 就不用 ORDER BY慎用 DISTINCT去重操作消耗大量资源能用 GROUP BY 代替就用 GROUP BY慎用 UNION需要合并去重开销大确定无重复时用 UNION ALLMap Join大表 JOIN 小表时把小表放入内存小表不能超过内存大小避免数据倾斜解决 key 分布不均的问题详见上一章的数据倾斜部分合理设置参数调整 map/reduce 相关参数set hive.map.aggrtrue;6.5 Map Join 详解什么是 Map Join传统 JOIN 需要经过 shuffle 阶段Map → Shuffle → ReduceMap Join 把小表加载到内存在 Map 阶段直接完成 JOIN避免 shuffle。如何使用sql-- 方式1自动 Map JoinHive 会自动判断 set hive.auto.convert.jointrue; set hive.mapjoin.smalltable.filesize25000000; -- 小表阈值 25MB -- 方式2手动指定Hive 的 map join 语法 select /* mapjoin(a) */ * from 小表 a left join 大表 b on a.id b.id;七、面试常见问题大表关联大表跑不过怎么办问题场景两个都是大表TB 级别执行 JOIN 时出现内存不足、任务失败。解决方案汇总方案说明适用场景调整 JVM 内存增加 reducer 的内存大小内存不够但数据量可控拆表关联将一张大表拆成 N 张小表逐个关联后合并大表可按某个维度拆分分桶优化对大表按关联键预先分桶两表按相同键分桶可避免 shuffleMap Join如果大表可以过滤成小表关联前能大幅过滤数据详细方案解释方案一调整 JVM 内存bash# 修改 hive-site.xml 或执行 set 命令 set mapreduce.map.memory.mb4096; # map 内存 4GB set mapreduce.reduce.memory.mb8192; # reduce 内存 8GB方案二拆表关联sql-- 将大表 A 按某个维度拆成 N 张小表 -- 例如按部门拆分成 A_dept10, A_dept20, A_dept30 -- 逐个关联 insert into tmp1 select /* mapjoin(a) */ * from A_dept10 a left join 大表B b on a.id b.id; insert into tmp2 select /* mapjoin(a) */ * from A_dept20 a left join 大表B b on a.id b.id; -- 最后合并所有结果 insert into result select * from tmp1 union all select * from tmp2 union all ...方案三分桶优化sql-- 创建分桶表时让两张表按相同的键分桶桶数相同 create table A_bucketed ( id int, name string ) clustered by (id) into 128 buckets; create table B_bucketed ( id int, name string ) clustered by (id) into 128 buckets; -- 分桶后 JOIN相同 id 的数据已在同一 bucket避免 shuffle insert into result select * from A_bucketed a join B_bucketed b on a.id b.id;方案四先过滤再关联sql-- 如果大表关联前能大幅过滤数据先用子查询过滤 insert into result select * from (select * from 大表A where date 2024-01-01) a left join (select * from 大表B where date 2024-01-01) b on a.id b.id;八、面试常见问题Hive 优化参数汇总参数作用建议值hive.auto.convert.join自动 Map Jointruehive.mapjoin.smalltable.filesize小表阈值2500000025MBhive.exec.dynamic.partition.mode动态分区模式nonstrict允许动态分区hive.exec.dynamic.partition是否开启动态分区truehive.groupby.skewindata处理 group by 数据倾斜truemapreduce.map.memory.mbMap 内存根据实际情况调整mapreduce.reduce.memory.mbReduce 内存根据实际情况调整九、命令速查表操作命令直接执行 SQLhive -e SELECT * FROM table执行 SQL 文件hive -f script.sql指定存储格式STORED AS orcMap Join 提示SELECT /* MAPJOIN(small_table) */ ...设置自动 Map Joinset hive.auto.convert.jointrue;设置动态分区set hive.exec.dynamic.partition.modenonstrict;设置 group by 倾斜处理set hive.groupby.skewindatatrue;十、核心概念总结概念一句话总结hive -e直接在 Linux 命令行执行 Hive SQLhive -f执行文件中的 Hive SQLORC 格式列式存储压缩比高适合大数据量ORDER BY全局排序只有一个 reducerSORT BY每个 reducer 内部排序DISTRIBUTE BY按指定字段分发到不同 reducerCLUSTER BY分发 排序同一字段Map Join把小表放内存避免 shuffle数据倾斜key 分布不均导致某些节点负载过高

相关新闻