京东够狠:100W数据去重,用distinct 还是group by,哪个效率更YYDS

文摘   2024-08-01 19:58   四川  

人和人之间

有时候只要有一个moment

就够了



近段时间,身边有不少的人都失业了,然而在IT行业,把boss直聘都刷爆了,也就几个面试机会,很多都是未读或已读不回。


最近认识的一个小伙伴终于有个京东的面试,但是他回来的时候感觉这次面试可能又要挂了。


他觉得下面这个问题他答的很不好。

100W数据去重,是用distinct还是group by,说说理由?


当时听到这个问题的时候,他就感觉不妙了。


这个问题实际上是在考验在大数据情况下如何对mysql进行优化的问题。同时也需要对distinct和group by有一些深层的认知。


首先,来重新认识一下distinct

distinct给我们的直观感受是去除 SELECT 语句返回结果中重复行的关键字。


比如下面的返回结果会出现名字一样的结果

SELECT name FROM users;


那么我们可以用distinct来对它进行去重

SELECT DISTINCT name FROM users;


需要注意的是,DISTINCT 关键字会对查询的性能产生一定的拉垮作用,因为它需要对结果集进行排序和去重的操作。所以要尽可能地使用索引来优化查询,以提高查询的性能。


distinct的另一个特点是它必须放在查询语句中的第一个字段前使用,且作用于主句所有列。会将所有NULL值视为相同的值。


再来认识一下group by

GROUP BY 主要的使用场景是在分组和聚合。一般出现在具有统计业务的地方。


GROUP BY 子句通常用于将查询结果按照一个或多个列进行分组,然后通过调用聚合函数对每个组进行聚合计算。当然也可以使用having关键字对每个分组进行过滤。


例如,假设一个表存储了用户的姓名、年龄和所在城市,可以使用 GROUP BY 子句按照城市对人进行分组,并计算每个城市的平均年龄或人口数量或者其他的统计操作。


GROUP BY 子句通常与聚合函数一起使用,如 COUNT、SUM、AVG、MAX 和 MIN。例如,可以使用 GROUP BY 子句和 COUNT 函数来计算每个城市中的人数。或者每个年龄对应的人数。

select city ,count(*) as num from user group by city;


下面来看看它的执行计划

explain select city ,count(*) as num from user group by city;



有2个点需要注意

第一个,在 Extra 字段里面,出现了Using temporary,这表示执行时创建了一个内部临时表,如果数据小,mysql会创建一个内存临时表来进行计算,如果数据大,mysql会在硬盘创建临时表,这也意味着有更多的性能消耗。


第二个,在 Extra 字段里面,出现了Using filesort, 表示执行过程中没有使用索引的排序,而是使用临时文件。


那么group by是如何实现去重的呢?


在执行过程中首先创建内存临时表,表里有city, num两个字段,city为主键。


扫描user表,每次从里面取出一条数据,如果此时取出的city的值是a,那么后面再取到a的时候,会更新临时表的num,对它进行加1操作。如果是新的值,则插入。


扫描完了之后,再对临时表的数据根据city进行排序。




和DISTINCT子句一样,group by将所有NULL值视为相同的值。


通过比较,如果distinct和group by 都用上了索引,那么它们的效率是不相上下的。


如果是无索引,那么distinct的效率要远高于group by。因为group by 还需要filesort。


但是京东的面试题不至于这么简单。


上面的结论针对mysql8是可用的,因为mysql8取消了group by的隐式排序。所以在mysql8以后,它们之间的效率也近乎一样了。


但是对于大多数情况,我们推荐使用group by,因为它可对数据进行更为复杂的一些处理,灵活性更高。






golang学习记
分享golang 学习的点滴
 最新文章