字节面试手撕题分享
昨天也是被字节二面狠狠拷打了,问了一些Spark的技术细节和很多在上家公司的工作内容和项目架构,压力山大,不过感谢面试官捞我,算是通过了。
ok,现在分享一下当时出的一道手撕题:
存在表tablea(uid, cookie_id), 其中uid和cookie_id是联合主键 存在表tableb(uid, order_id), 其中uid和order_id是联合主键 求(uid, cookie_id_cnt, order_id_cnt) 性能越快越好
当时首先想到是对两张表分别分组聚合再join
select COALESCE(a.uid, b.uid) AS uid, -- 取存在的uid(处理仅在某一表存在的uid) COALESCE(a.cookie_id_cnt, 0) AS cookie_id_cnt, -- 无数据则为0 COALESCE(b.order_id_cnt, 0) AS order_id_cnt -- 无数据则为0 from ( select uid, count(cookie_id) as cookie_id_cnt from tablea group by uid ) as aaa full outer join ( select uid, count(order_id) as order_id_cnt from tableb group by uid ) as bbb
这是最简陋的方法,唯一能说的性能优化点几乎是先进行了聚合,这样后续对两张表的join时会减少数据的shuffle量。但是还是有shuffle存在。
将两张表的数据合并在一起,除了join之外还有其他的方式吗?
这个问题其实在刷题网站上还是比较少见的,答案是有。join可以看成是横向合并,union系列可以看作是纵向合并。但是这又有一个问题,union系列操作对两张表纵向合并的时候,需要两张表的schema结构一致。而tablea和tableb的schema其实并不一致。这个时候就涉及到一个经典问题了 **是要时间还是要空间**,如果我们不管存储的开销,只为了计算更快的话,那我们就直接把两张表的schema变成一样的就行了嘛,而且在我们大数据场景下,存储的资源是较多的,所以以后做业务的时候要优先考虑到计算时间的问题。想清楚这些,就能得到一个不join的版本。
SELECT distinct uid, sum(cookie_id_cnt) AS cookie_id_cnt, sum(order_id_cnt) AS order_id_cnt FROM ( -- 处理tablea:每条记录对应1个cookie_id SELECT uid, 1 AS cookie_id_cnt, 0 AS order_id_cnt FROM tablea UNION ALL SELECT uid, 0 AS cookie_id_cnt, 1 AS order_id_cnt FROM tableb ) AS combined GROUP BY uid;
做手撕题的时候,最好把你的思路跟面试官讲清楚,
比如为什么不想用join?
因为大表之间的join会进行很多shuffle操作,很浪费资源。
如果大表不得不join,你有什么好办法去优化性能呢?
这时候你就要想到大表join大表的常用优化方案(分桶排序再join)。
为什么分桶排序再join能优化性能?
这时候你要想到join为什么会导致大量的shuffle,原始是最开始读数据的时候是按照偏移量去读的,我们做表的join是,要把相同key的数据分区到同一个partition,涉及数据跨分区的重新分布就要用到shuffle,分桶之后就将数据先按照相同key分好,这样Spark读数据的时候,就能将按照事先通过桶分好的相同key的数据读到同一个分区,从而避免shuffle。
#数据人的面试交流地##数据人offer决赛圈怎么选#