案例
方式1
使用group by
select s_age,count(1) from( select s_age from student_tb_orc group by s_age ) b
方式2
使用distinct
select s_age,count(distinct s_age) from student_tb_orc
分析
数据量特别大
方式1的代码在数据量特别大的情况下能够有效避免 Reduce 端的数据倾斜,此时可能会比案例方式2效率高。
采用方式1的写法,什么时候会比方式方式2高呢?
在有数据倾斜的情况下,案例1 的方式会比方式方式2更优。什么是数据倾斜?是指当所需处理的数据量级较大时某个类型的节点所需要处理的数据量级,大于同类型的节点个数量级 (10 倍) 以上这里的某个类型的节点可以指代执行 Map 或者 Reduce 的节点当数据大到一定的量级时,方式1 有两个作业,可以把处理逻辑分散到两个阶段中,即第一个阶段先处理一部分数据,缩小数据量,第二个阶段在已经缩小的数据集上继续处理。
而方式方式2,经过 Map 阶段处理的数据还非常多时,所有的数据却都需要交给个 Reduce 节点去处理,就好比千军万马过独木桥一样,不仅无法利用到分布式集群的优势,还要浪费大量时间在等待,而这个等待的时间远比方式1 多个 MapReduce 所延的流程导致额外花费的时间还多
数据量小/key明显有限
我们先不管数据量特别大这个问题,就当前的业务和环境下,方式2 一定会比方式1 的效率高,原因有以下几点:
-
进行去重的列是 s_age 列,它的业务含义表示年龄。既然是年龄,说明它的可枚举值非常有限,如果转化成 MapReduce 来解释的话,在 Map 阶段,每个 Map 会对 s_age去重。由于 s_age 枚举值有限,因而每个 Map 得到的 s_age 也有限,最终得到 reduce 的数据量也就是 map 数量s_age 举值的个数。假如执行方式1的代码 Map 的数量有 100个s_age 的最大举值有 100个每个Map 过滤后的数据都含有 s_age 的所有举值且s_age是int型4个字节,那么传输到 Reduce 的数据量就是 10000 条记录,总数据量是 40KB,这么小的数据量,不需要避免数据倾斜。假如执行方式1的代码 Map 的数量有 100个 s_age 的最大举值有 100个每个Map 过滤后的数据都含有 s_age 的所有举值且s_age是int型4个字节,那么传输到 Reduce 的数据量就是 10000 条记录,总数据量是 40KB,这么小的数据量,不需要避免数据倾斜。
-
方式2 中,distinct 的命令会在内存中构建一个 hashtable,查找去重的时间复杂度是 0(1);
方式1中,group by 在不同版本间变动比较大,有的版本会用构建 hashtable 的形式去重,有的版本会通过排序的方式,排序最优时间复杂度无法到 O(1)。另外,方式1会转化为两个任务,会消耗更多的磁盘网络I/O资源。 -
最新的 Hive 3.0 中新增了 count (distinct)优化,通过配置 hive.optimize.countdistinct,即使真的出现数据倾斜也可以自动优化,自动改变SOL执行的逻辑。
-
方式2 比方式1 代码简洁,表达的意思简单明了,如果没有特殊的问题代码简洁就是优。