前言
我们经常做去重的操作,事实上几种方式可以实现去重,但是结果的理解其实是不一样的,不过在一定程度上这几种也都可以满足我们的需求。
参考数据
id | name |
1 | a |
2 | b |
1 | a |
1 | b |
Distinct去重
这个其实是真正的去重,语意上其实就是如果出现一样的结果,则就显示一行
可以作用单行或者多行
select distinct id from t;
id |
1 |
2 |
或者是
select distinct id,name from t;
作用多行的时候多行要一起相等才行
id | name |
1 | a |
2 | b |
1 | b |
group by操作
实际上这个要求去重列作为聚合字段,我们一般可以做聚合去实现
select count(*) as cnt,id,name from t group by id,name;
当我们不关心聚合结果的时候得到的结果就是去重的结果了
row_number开窗操作
实际上我们要分开来看,首先是开窗之后的结果
select row_number() over (partition by id) as rn ,id,name from t
rn | id | name |
1 | 1 | a |
2 | 1 | b |
3 | 1 | b |
1 | 2 | b |
这个操作会把相同的id增加一个序号列,我们一般是外面套一个过滤条件去筛选
select * from ( select row_number() over (partition by id) as rn ,id,name from t ) h where h.rn=1;
这种做法实际上是在所有记录中取一条记录。
性能分析
事实上直接的distinct的话直接做一次reduceByKey就可以了,简单来说,这个在map端是可以优化
当然优化都是建立在数据量大的时候才有意义,当数据输出比较大的时候,map端可以多做一步
同样的道理,我们做group by其实也可以这样子做,这个技术其实是combine技术,减少网络传输
关键的前提是允许这么干才行,否则是拿不到要的结果的,row_number()的操作类似这样子的,需要提前编号,再进行过滤
从图中就可以发现,中间过程的要求,导致我们不能做一个map操作的优化,所以其实是会慢一些,但是row_number实际上会保留我们需要的那行数据本身,所以大部分情况是不能被group by替代的
总结
其实几种去重的方式本质上意义是不一样的,这也是满足不同的需求下的操作,表面上差不多,实际考虑实现的时候还是要多琢磨一下。