人和人之间
有时候只要有一个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,因为它可对数据进行更为复杂的一些处理,灵活性更高。