Hive四种排序深度解析:ORDER BY、SORT BY、DISTRIBUTE BY、CLUSTER BY

Hive四种排序深度解析:ORDER BY、SORT BY、DISTRIBUTE BY、CLUSTER BY

    • 一、四种排序直观对比
      • 1.1 一张图看懂区别
      • 1.2 核心区别速查表
    • 二、ORDER BY:全局排序
      • 2.1 工作原理
      • 2.2 基本语法
      • 2.3 strict模式下的限制
      • 2.4 ORDER BY的性能优化
    • 三、SORT BY:局部排序
      • 3.1 工作原理
      • 3.2 基本语法
      • 3.3 SORT BY与LIMIT的配合
      • 3.4 实际应用场景
    • 四、DISTRIBUTE BY:数据分发
      • 4.1 工作原理
      • 4.2 基本语法
      • 4.3 与GROUP BY的区别
      • 4.4 解决数据倾斜的经典用法
    • 五、CLUSTER BY:分发+排序
      • 5.1 工作原理
      • 5.2 基本语法
      • 5.3 CLUSTER BY的限制
    • 六、四种排序组合使用
      • 6.1 DISTRIBUTE BY + SORT BY(最常用)
      • 6.2 实际执行计划对比
      • 6.3 排序方式选择指南
    • 七、实战案例:各种排序的效果对比
      • 7.1 测试数据准备
      • 7.2 各种排序效果对比
    • 八、面试高频问题
      • Q1:ORDER BY为什么一定要加LIMIT?
      • Q2:SORT BY和ORDER BY的结果有什么区别?
      • Q3:DISTRIBUTE BY和GROUP BY有什么区别?
      • Q4:CLUSTER BY和DISTRIBUTE BY + SORT BY完全等价吗?
      • Q5:如何实现每个分组内排序,但分组间随机?
    • 九、总结
      • 9.1 记忆口诀
      • 9.2 选择指南
      • 9.3 性能对比

🌺The Begin🌺点点关注,收藏不迷路🌺

关键词:Hive排序、全局排序、局部排序、数据分发、分区排序、查询优化、执行计划

在Hive SQL中,排序是日常查询中最常用的操作之一。但Hive提供了四种不同的排序方式,每种都有其特定的用途和性能特征。理解它们的区别,对于写出高效、正确的Hive查询至关重要。

今天,我们将深入剖析这四种排序的本质区别、使用场景以及性能影响,帮助你在实际工作中做出正确的选择。


一、四种排序直观对比

1.1 一张图看懂区别

CLUSTER BY

按字段哈希分发+排序

按字段哈希分发+排序

按字段哈希分发+排序

所有数据

Reducer1
分发并排序

Reducer2
分发并排序

Reducer3
分发并排序

DISTRIBUTE BY

按字段哈希分发

按字段哈希分发

按字段哈希分发

所有数据

Reducer1
相同key到一起

Reducer2
相同key到一起

Reducer3
相同key到一起

SORT BY

多个Reducer

多个Reducer

多个Reducer

所有数据

Reducer1
局部排序

Reducer2
局部排序

Reducer3
局部排序

文件1有序

文件2有序

文件3有序

ORDER BY

强制1个Reducer

所有数据

全局排序
一个文件

结果全局有序

1.2 核心区别速查表

排序方式 Reducer数 全局有序 局部有序 数据分组 适用场景
ORDER BY 1个 小数据量全局排序
SORT BY 多个 每个文件需要有序
DISTRIBUTE BY 多个 数据分发、解决倾斜
CLUSTER BY 多个 分组后组内排序

二、ORDER BY:全局排序

2.1 工作原理

ORDER BY执行过程

Shuffle所有数据

全局排序

海量输入数据

Map阶段

单个Reducer

一个有序文件

特点

  • 强制使用1个Reducer:无论数据量多大,只有一个Reducer处理
  • 全局有序:所有数据严格按照指定字段排序
  • 性能瓶颈:数据量大时极慢,可能OOM

2.2 基本语法

-- 基础ORDER BY
SELECT user_id, amount, order_time
FROM user_orders
ORDER BY amount DESC;
-- 多字段排序
SELECT user_id, amount, order_time
FROM user_orders
ORDER BY user_id ASC, amount DESC;

2.3 strict模式下的限制

-- Hive的strict模式
SET hive.mapred.mode=strict;  -- 开启严格模式
-- 在strict模式下,ORDER BY必须加LIMIT
SELECT * FROM user_orders ORDER BY amount DESC;
-- 报错!FAILED: SemanticException 1:48 
-- Order by without limit may cause out of memory error.
-- 必须加LIMIT
SELECT * FROM user_orders 
ORDER BY amount DESC 
LIMIT 1000;  -- ✅ 正确
-- nonstrict模式(默认)
SET hive.mapred.mode=nonstrict;  -- 可以不加LIMIT,但不推荐

为什么strict模式强制LIMIT?

  • ORDER BY只有一个Reducer
  • 海量数据全部到一台机器,可能内存溢出
  • 加LIMIT后,可以在Map端先过滤,只取TOP N到Reducer

2.4 ORDER BY的性能优化

-- 优化1:总是加LIMIT
SELECT * FROM large_table 
ORDER BY amount DESC 
LIMIT 100;  -- 只取前100条
-- 优化2:如果必须全排序,增加Reducer内存
SET mapreduce.reduce.memory.mb=4096;
SET mapreduce.reduce.java.opts=-Xmx3686m;
-- 优化3:使用TBLPROPERTIES优化排序
CREATE TABLE sorted_table 
TBLPROPERTIES ('ORDER BY'='amount DESC');

三、SORT BY:局部排序

3.1 工作原理

SORT BY执行过程

随机/默认分发

随机/默认分发

随机/默认分发

内部排序

内部排序

内部排序

输入数据

Map阶段

Reducer 1

Reducer 2

Reducer 3

有序文件1

有序文件2

有序文件3

特点

  • 多个Reducer:可设置多个Reducer并行处理
  • 局部有序:每个Reducer的输出文件内部有序
  • 全局无序:文件之间没有顺序关系

3.2 基本语法

-- 设置Reducer数量
SET mapreduce.job.reduces=3;
-- SORT BY查询
SELECT user_id, amount, order_time
FROM user_orders
SORT BY amount DESC;
-- 结果:生成3个文件,每个文件内按amount降序排列
-- 文件1: (1000, 'user1'), (800, 'user2'), (500, 'user3')
-- 文件2: (900, 'user4'), (700, 'user5'), (600, 'user6')
-- 文件3: (950, 'user7'), (850, 'user8'), (400, 'user9')

3.3 SORT BY与LIMIT的配合

-- SORT BY + LIMIT 的优化效果
SELECT user_id, amount
FROM user_orders
SORT BY amount DESC
LIMIT 100;
-- 每个Reducer只取前100条,然后合并
-- 传输到Reducer的数据量 = 100 * Map数
-- 大大减少数据量!
-- 执行计划中的优化
EXPLAIN
SELECT * FROM user_orders
SORT BY amount DESC
LIMIT 100;
-- 可以看到每个Map输出前会先取TOP 100

3.4 实际应用场景

-- 场景1:导出数据,每个文件需要有序
INSERT OVERWRITE DIRECTORY '/output/orders'
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
SELECT order_id, user_id, amount
FROM user_orders
SORT BY order_time;  -- 每个输出文件内按时间排序
-- 场景2:配合DISTRIBUTE BY使用(见下一节)

四、DISTRIBUTE BY:数据分发

4.1 工作原理

DISTRIBUTE BY执行过程

按字段哈希分发

按字段哈希分发

按字段哈希分发

输入数据

Map阶段

Reducer 1
key=A的所有数据

Reducer 2
key=B的所有数据

Reducer 3
key=C的所有数据

特点

  • 控制数据分发:相同字段值进入同一个Reducer
  • 不保证排序:只负责分发,不排序
  • 常用于解决数据倾斜:自定义分发逻辑

4.2 基本语法

-- 基础DISTRIBUTE BY
SELECT user_id, amount, order_time
FROM user_orders
DISTRIBUTE BY user_id;
-- 同一个user_id的所有订单进入同一个Reducer
-- 使用函数进行分发
SELECT user_id, amount, order_time
FROM user_orders
DISTRIBUTE BY length(user_id);
-- 按user_id长度分发,相同长度的进入同一Reducer
-- 最常用:DISTRIBUTE BY + SORT BY 组合
SELECT user_id, amount, order_time
FROM user_orders
DISTRIBUTE BY user_id
SORT BY amount DESC;
-- 每个用户的订单进入同一Reducer,并在Reducer内按金额排序

4.3 与GROUP BY的区别

-- DISTRIBUTE BY:只分发,不聚合
SELECT user_id, amount
FROM user_orders
DISTRIBUTE BY user_id;
-- 结果:每个user_id的数据在一起,但数据条数不变
-- GROUP BY:分发并聚合
SELECT user_id, COUNT(*), SUM(amount)
FROM user_orders
GROUP BY user_id;
-- 结果:每个user_id一条记录,包含聚合值

4.4 解决数据倾斜的经典用法

-- 场景:某个Key(如NULL)特别多,导致Reducer倾斜
-- 优化前:所有NULL进入同一Reducer
SELECT 
    CASE WHEN user_id IS NULL THEN 'NULL' ELSE user_id END,
    COUNT(*)
FROM user_actions
GROUP BY 
    CASE WHEN user_id IS NULL THEN 'NULL' ELSE user_id END;
-- 优化后:NULL值加随机后缀,分散到不同Reducer
SELECT 
    CASE 
        WHEN user_id IS NULL 
            THEN CONCAT('NULL_', CAST(CEIL(RAND()*10) AS STRING))
        ELSE user_id
    END AS user_key,
    COUNT(*) AS cnt
FROM user_actions
GROUP BY 
    CASE 
        WHEN user_id IS NULL 
            THEN CONCAT('NULL_', CAST(CEIL(RAND()*10) AS STRING))
        ELSE user_id
    END;

五、CLUSTER BY:分发+排序

5.1 工作原理

CLUSTER BY执行过程

按字段哈希分发+排序

按字段哈希分发+排序

按字段哈希分发+排序

输入数据

Map阶段

Reducer 1
key=A且有序

Reducer 2
key=B且有序

Reducer 3
key=C且有序

特点

  • DISTRIBUTE BY + SORT BY 的组合
  • 字段必须相同:分发和排序用同一个字段
  • 只能升序:无法指定ASC/DESC,默认升序

5.2 基本语法

-- CLUSTER BY
SELECT user_id, amount
FROM user_orders
CLUSTER BY user_id;
-- 完全等价于
SELECT user_id, amount
FROM user_orders
DISTRIBUTE BY user_id
SORT BY user_id;  -- 注意:SORT BY字段必须和DISTRIBUTE BY相同
-- 不等价于(因为SORT BY字段不同)
SELECT user_id, amount
FROM user_orders
DISTRIBUTE BY user_id
SORT BY amount;  -- 这是不同的语义!

5.3 CLUSTER BY的限制

-- 无法指定排序方向
SELECT user_id, amount
FROM user_orders
CLUSTER BY user_id DESC;  -- ❌ 语法错误!
-- 如果想降序,必须拆开写
SELECT user_id, amount
FROM user_orders
DISTRIBUTE BY user_id
SORT BY user_id DESC;  -- ✅ 正确
-- CLUSTER BY只能升序
SELECT user_id, amount
FROM user_orders
CLUSTER BY user_id;  -- 等价于 SORT BY user_id ASC

六、四种排序组合使用

6.1 DISTRIBUTE BY + SORT BY(最常用)

-- 需求:每个部门内按工资排序
SELECT 
    dept_id,
    emp_name,
    salary
FROM employee
DISTRIBUTE BY dept_id  -- 按部门分发
SORT BY salary DESC;   -- 每个部门内按工资降序
-- 结果:每个部门的员工在一起,且按工资从高到低排序

6.2 实际执行计划对比

-- 查看不同排序的执行计划
EXPLAIN
SELECT * FROM employee
ORDER BY salary DESC;
EXPLAIN
SELECT * FROM employee
SORT BY salary DESC;
EXPLAIN
SELECT * FROM employee
DISTRIBUTE BY dept_id
SORT BY salary DESC;
EXPLAIN
SELECT * FROM employee
CLUSTER BY dept_id;

6.3 排序方式选择指南

需求 推荐组合 原因
结果需要全局有序 ORDER BY 唯一选择,但注意加LIMIT
每个输出文件需要有序 SORT BY 多个Reducer并行,性能好
把相同key放在一起 DISTRIBUTE BY 为后续处理做准备
相同key放在一起并排序 CLUSTER BY 或 DISTRIBUTE BY + SORT BY 分组聚合前的预处理
分组内排序 DISTRIBUTE BY + SORT BY 最灵活,可指定不同字段

七、实战案例:各种排序的效果对比

7.1 测试数据准备

-- 创建测试表
CREATE TABLE test_sort (
    id INT,
    category STRING,
    value INT
);
-- 插入测试数据
INSERT INTO test_sort VALUES
(1, 'A', 100),
(2, 'A', 80),
(3, 'B', 90),
(4, 'B', 70),
(5, 'A', 60),
(6, 'C', 95),
(7, 'C', 85),
(8, 'B', 75),
(9, 'C', 65),
(10, 'A', 55);

7.2 各种排序效果对比

-- 1. ORDER BY category, value
-- 结果:全局有序,1个文件
-- A,55; A,60; A,80; A,100; B,70; B,75; B,90; C,65; C,85; C,95
-- 2. SORT BY value (设置3个Reducer)
-- 结果:3个文件,每个文件内按value排序
-- 文件1: 可能包含 A,55; A,60; B,70; B,75 等(取决于分发)
-- 文件2: 可能包含 A,80; C,85; B,90; C,95
-- 文件3: 可能包含 A,100 等
-- 3. DISTRIBUTE BY category
-- 结果:每个category进入同一个Reducer
-- Reducer1(A): 1-A-100, 2-A-80, 5-A-60, 10-A-55 (顺序不确定)
-- Reducer2(B): 3-B-90, 4-B-70, 8-B-75
-- Reducer3(C): 6-C-95, 7-C-85, 9-C-65
-- 4. CLUSTER BY category
-- 结果:每个category进入同一个Reducer,且按category排序
-- Reducer1(A): 1-A-100, 2-A-80, 5-A-60, 10-A-55 (按category升序,实际就是A)
-- Reducer2(B): 3-B-90, 4-B-70, 8-B-75
-- Reducer3(C): 6-C-95, 7-C-85, 9-C-65
-- 5. DISTRIBUTE BY category SORT BY value DESC
-- 结果:每个category进入同一个Reducer,且按value降序
-- Reducer1(A): 1-A-100, 2-A-80, 5-A-60, 10-A-55
-- Reducer2(B): 3-B-90, 8-B-75, 4-B-70
-- Reducer3(C): 6-C-95, 7-C-85, 9-C-65

八、面试高频问题

Q1:ORDER BY为什么一定要加LIMIT?

:因为ORDER BY强制使用1个Reducer处理所有数据。如果不加LIMIT:

  • 海量数据全部到一台机器,可能内存溢出
  • 处理时间极长,失去分布式计算的意义
  • 在strict模式下,Hive强制要求加LIMIT以避免这些问题

Q2:SORT BY和ORDER BY的结果有什么区别?

  • ORDER BY:输出一个文件,所有数据全局有序
  • SORT BY:输出多个文件,每个文件内部有序,但文件之间无序

举例:数据 [5,2,8,1,9,3]

  • ORDER BY输出一个文件:[1,2,3,5,8,9]
  • SORT BY输出3个文件(假设):文件1:[2,5,8] 文件2:[1,3,9] 整体看:2,5,8,1,3,9 不是全局有序

Q3:DISTRIBUTE BY和GROUP BY有什么区别?

  • DISTRIBUTE BY:只控制数据如何分发到Reducer,不进行聚合计算,数据条数不变
  • GROUP BY:在分发的基础上进行聚合计算,每组输出一条记录

Q4:CLUSTER BY和DISTRIBUTE BY + SORT BY完全等价吗?

:不完全等价,有两个关键区别:

  1. 字段必须相同:CLUSTER BY要求分发和排序字段相同,而DISTRIBUTE BY + SORT BY可以不同
  2. 排序方向限制:CLUSTER BY只能升序,而DISTRIBUTE BY + SORT BY可以指定DESC

Q5:如何实现每个分组内排序,但分组间随机?

-- 最佳实践:DISTRIBUTE BY + SORT BY
SELECT 
    category,
    id,
    value
FROM test_sort
DISTRIBUTE BY category  -- 相同category进入同一Reducer
SORT BY value DESC;      -- 每个Reducer内按value排序

九、总结

9.1 记忆口诀

全局排序ORDER BY,一个Reducer拖到底
局部排序SORT BY,每个文件自己比
数据分发DISTRIBUTE BY,相同key到一起
分发排序CLUSTER BY,两者结合限制你

9.2 选择指南

需求 选择 原因
结果需要全局有序 ORDER BY 唯一选择,但注意加LIMIT
每个输出文件需要有序 SORT BY 多个Reducer并行,性能好
把相同key放在一起 DISTRIBUTE BY 为后续处理做准备
相同key放在一起并排序 CLUSTER BY 或 DISTRIBUTE BY + SORT BY 分组聚合前的预处理
分组Top-N DISTRIBUTE BY + SORT BY + ROW_NUMBER 经典组合

9.3 性能对比

查询类型 数据量 Reducer数 执行时间
ORDER BY 100GB 1 30分钟
SORT BY 100GB 50 2分钟
DISTRIBUTE BY 100GB 50 1.5分钟
CLUSTER BY 100GB 50 2分钟

掌握这四种排序的区别,你就能在Hive查询中游刃有余,既能保证结果的正确性,又能兼顾查询性能!


思考题:在Hive 3.0中,ORDER BY配合LIMIT时做了哪些优化?为什么加了LIMIT的ORDER BY可以不用把所有数据都发送到一个Reducer?欢迎在评论区讨论!

在这里插入图片描述

🌺The End🌺点点关注,收藏不迷路🌺
© 版权声明

相关文章