Shopee面试复盘

背景

了解到Shopee最初是在其他dalao的面经、V2EX上。因为2月份字节跳动的面试失利,而且结合2020年年初整体环境的情况,所以打算做个100 Day Countdown复习再尝试的,不过综合考虑觉得金三银四的机会错过可能就没有了,最后在三月初的时候找dalao内推了Shopee的岗位。同期在考虑的还有网易和360的一些岗位,网易在广州的主要是游戏岗,技术栈上Match的程度会相对低一些,而且了解到部分目标岗位入职之后貌似是以Python为主,而我目前做的也是Python开发,但是更倾向于接触Go和Go的生态(云原生),这个也是优先考虑Shopee机会的原因之一。

面试

因为赶上春招,所以大概面试官们都比较忙,投递之后大约过了2周HR电话联系,约了一面时间。

一面(1小时17分钟)

一面我要求在了一个比较晚的时间,因为Shopee晚上是不面试的,所以定在了下午大概下班前一点的时候,然而没有想到一聊就聊了接近80分钟,结束的时候已经接近7点了。

  • 自我介绍
  • 介绍基本的项目架构,问了Elasticsearch在项目中的用途
  • 每日新增数据量*00w,不算少了,用MySQL怎么样做的?
    • 看具体业务,例如新增这块是某些业务数据的快照,用作趋势图
    • 新增数据多,分策略处理,按业务分区,定时归档到oss
  • 除去归档之后数据还有多大?以什么样的方式检索?
    • 除去归档之后的量级在千万级
    • 虽然有千万,但是根据业务查询做Partition,每个tablespace不大,使用xx作为主键,查询也按照主键,速度可以接受
  • 对Percona和它MySQL的分支了解多少?它改表的工具有看过吗?原理能说一下吗,改表过程中一致性如何保证?
    • Percona的工具一般都是运维在用,自己RSS了他们的博客,阅读和翻译感兴趣的文章
    • pt-osc,改表通过创建新表,复制旧表数据最后原子操作RENAME替换完成。(答得不在点子上,最重要的通过Trigger保障过程中的一致性没有提出来)
  • 有做过分享过BloomFilter,分享的原理还是应用?
    • Both
    • 概率型过滤器,业务上用做去重判定,有在团队里面推广,目前在新业务上准备尝试
    • Redis中使用Bitmap实现,对判断内容Hash置位,如果对应位置都已经置位过说明元素可能存在,反之必然不存在
  • 用过Redis的哪些数据结构?redis-cell是什么?
    • 除了基础的5种结构以外,还尝试过HyperLogLog,布隆过滤器,redis-cell,stream
    • redis-cell是一个外挂模块,漏斗模型限流
  • 有用过Redis的Cluster吗?了解原理吗?如果有节点挂了会怎么样?
    • 业务使用Redis的部分数据量比较少所以用不上,自己有尝试过
    • 基于槽分配,将集群划分成16384个槽,分配给不同节点,当所有槽分配完毕的时候集群上线
    • 如果用官方工具创建类似典型的三主三从集群,主节点挂掉之后会自动有从节点顶上
  • 用Redis不同的数据结构都实现过什么业务?
    • HyperLogLog,和BloomFilter相似,场景为概率型的计数,例如超大量的每日访问IP数(PV),代替set类型
    • ZSET,做过排行榜
    • String,做日常缓存
  • 了解Redis HASH结构的实现吗?怎么保证查找的复杂度是O(1)?
    • 底层是一个字典
    • Emmm…(场面一度尴尬,觉得很基础又一时说不上来,最后猜测是Hash之后通过内存地址查找所以是O(1))不太了解

然后聊到这里附近的时候远程聊天网络原因断开了一下orz,多给了一点点思考的时间,虽然重连之后还是答得不太满意。

  • 有用过短域名服务吗,能说一下吗?
    • (简历上写了一个TinyURL系统架构设计的博客,不知道面试官是看到了所以对这方面感兴趣,还是这么凑巧他想问一个我设计过的架构)
    • 方案:自增ID + Base62
    • 细节:自增ID肯定是唯一的,问题在于如何保证在分布式系统中不同节点的自增ID没有重复,使用ZooKeeper提前对号段进行划分,应用节点自行获取和内部维护
    • 比较:MD5、UUID、自增Base62,各有优势,使用HASH的方案需要考虑冲突问题,UUID太长
  • 大访问量的情况下这个业务怎么设计?
    • 短链有失效时间吗?(一天内访问非常频繁,后面急剧下降)
    • 那将生成的数据放到Cache中设置1天TTL,Cache不存在回MySQL查(这里有遗漏的点没有答好,场景下会产生大量的短链长链对应关系,数据量级也需要考虑和处理,不可能无限增长下去,面试官提问的时候有强调到,但是回答之后貌似刚好也和我一样漏掉了XD)
  • 如果要求长域名一样的时候对应短域名也一样怎么设计?
    • 每次回表查关系,但是这样不理想
    • 思路是判定的时候需要查询,那么就想办法降低查询次数,没出现过的URL肯定会对应新短链,那么结合BloomFilter判定是否曾经出现过,如果出现过再回表确认真的出现还是BloomFilter的误判
  • 有试过BloomFilter数据量比较大的时候占用Redis空间有多少吗?
    • 很低,但是不知道具体数字(这里是个超级大坑,一面没有深究,二面的时候被追问细节,然而当初是想一二面一起复盘所以orz)
  • 业务已经很久,数据量大,需要做分表或者归档,会怎么做?
    • 归档貌似不行,旧数据还是要可用
    • 分表按照业务场景,查询是围绕短链ID,根据短链ID做hash分表而不是自增ID,这里视情况可能需要设计上用短链ID作为主键
  • 用Python里面的数据结构实现一个有序集合,思路
    • (答得不是很好,问到了讲的几个思路的复杂度的计算,自己打个50分不及格)
    • List + Dict(期望小于O(n)可以吗?)
    • …(求求自己回去多看看数据结构)
    • SkipList(真的不会,于是开始胡扯…)
  • 写SQL:找出A表存在,B表不存在的id
    • 写了一个NOT IN,强调性能不佳
    • 补充一个LEFT JOIN WHERE IS NULL的方案
    • 小表驱动大表
  • MySQL事务隔离级别?你们用的级别?可重复读是什么意思?
    • 4个
    • 可重复读
    • 解释了一下Read Commited里面不可重复读的场景,可重复读解决了这种问题,但是会存在幻读问题;强调InnoDB中使用Next-Key Locking,在Repeatable Read中就已经解决了幻读问题,解释了一下Gap Lock和Record Lock
  • 了解乐观锁与悲观锁吗?
    • 描述了一下以及讲一下怎么使用乐观锁
  • 了解InnoDB的索引实现?B+树和B树有什么区别?
    • B+树
    • 高扇出性,层数少、叶子节点带指针、B+树数据全部在叶子节点,B树索引节点也存数据
  • SQL题:哪个SQL能用上全部索引
  • 都是在用Python是吗?还会其他语言吗?
    • 读书的时候写PHP,现在在学Go
  • 对Python装饰器的理解?
    • (写了3行代码表达装饰器是对方法进行前置处理和后处理的夹心饼结构)
    • -(更好的表达应该是强调将被装饰对象作为参数传递,因为它是First Class Object,同时执行前后进行额外的逻辑,最后返回)
  • 写了一小段代码,问如何调用装饰器内部的定义的方法
  • 写了一小段闭包的代码,问执行结果
    • 答了一个错误的执行结果和正确的闭包概念
    • (我确认我是清晰理解了闭包的概念,但是思考的时候多操了一份心,又考虑了一些mutable object和immutable object的事情,结果就答错了结果,气坏了)
  • Python的functools和itertools有用过什么吗?Collections用到什么?
    • 有用过,但是一时想不起来里面的方法/对象了,经常用Collections
    • 用到defaultdict、OrderedDict、Counter等等
  • 进程与线程
  • Python的多线程可以用到多核,一般多线程用来做什么,多线程多进程用的什么库?
  • 了解HTTPS加密过程吗?讲一下握手的过程
  • 浏览器的缓存了解多少?自己建站静态文件会加缓存吗?
  • 编程题:Word Break和翻转二叉树
  • 有什么问题想问吗?
    • 对我的建议
    • 技术分享
    • 开源文化

一面结束之后过了几天接到HR电话,约了第二面的时间。

二面(45分钟)

二面是部门负责人面,问的问题相对少一些,但是抓的细节比较多,因为自己准备的方向(有一些知识点没有太细致的见解,因为不是科班背景,所以花了很多时间拓宽知识储备的广度,相对而言深度就只针对特定方向做了准备)没有在对应的点上,所以答得稀烂。

  • 自我介绍
  • 介绍基本的业务
  • 数据存储用的是什么?
  • 项目主要的难点是什么?快照是怎么做的,增量还是全量?全部数据(指的是动态和静态数据)都做快照吗?
  • 这些表都有多大?
  • 讲解一些表的设计的结构,字段类型

这里面试官对我描述的表的数据行数和体积有一些疑问,于是花了一点时间探讨,按照要求对描述的表进行了数据量的计算,最后得出来的结果和我提出的体积有比较大的差异。主要体现出来平时对业务数据量的预估和设计经验不够丰富,缺乏细致和准确性。面试的时候如果这些内容和对方经验值出入比较大被质疑,不可避免会拉低面试官的评价结果。然后引起了面试官一连串的疑问,包括像如果数据量没有描述的这么大,为什么要做分区?惭愧orz

  • 为了优化性能的话,是要控制表的大小还是控制表的行数?
  • Redis能够讲一下吗?
  • 大概讲一下BloomFilter?
  • BloomFilter的错误率有多少?(从这里开始后面就一直爆炸了)
    • 可以自己配置,受Bitmap长度和Hash函数影响
  • 具体会有多大?比如要让错误率在1/10000以下?
  • 具体和长度什么关系?和Hash函数个个数关系?
  • Hash函数一般选择用多少个?
    • (Orz这里我的理解和面试官的理解有所出入,我理解的模型是通过一个Hash函数计算出3个Bitmap需要置位的位置,而面试官认为的应该是通过3个不同的Hash函数计算出3个需要置位的位置,实际上都一样是需要计算3个置位的位置)
  • 一段递归代码,讲输出的结果
    • 写了正确的递归表达式,但是计算复杂度没有出结果
  • 递推式是正确的,时间复杂度?我们学算法的时候时间复杂度是怎么算的?
    • (非常惭愧没有掌握计算过程,噗甩锅跨专业,一定好好补充这块的知识)

二面技术问题基本就到这里结束了,大约30分钟,毫无疑问时间太短是个Bad Smell,可能意味着面试官已经认为不符合要求,打算结束。

面试全程都在“答不上来”和“不了解”之间切换,然后就到了跟面试官聊人生聊理想的环节,主要关注出来看工作机会的目的和考虑之类的问题。最后也解答了一下我的问题,问到面试官对我的建议,那么当然就是反复强调的细节了,技术细节需要更深入了解,惭愧。

挂掉二面通话之后感觉非常糟糕,很多问题看似简单(orz实际上真的不难)没有答得对甚至就没有能答出来,需要继续加油。

二面结束几个小时之后接到了HR的电话,约了聊人生的时间。

HR面(30分钟)

二面和HR面都是在同一天的,有点意外因为二面的表现实在是太糟糕了,不过也可能是因为需要综合评定,所以还有后续的流程要走完。HR面也是常规的一些问题提出来要了解,以及解答疑问,就不再详细描述了。

总结

整体来说考核的难度都比较正常,不过二面里面业务的掌握程度还是有所欠缺,一面里面的一些场景设计大概也不是特别理想。所以遵从面试官的建议,需要细抠技术细节,希望能够保持学习和成长。

面试感受:Pending有点久,大概是因为赶上毕业的同学们春招。
面试难度:正常,基础和业务结合。

最后许愿结果满意XD