前言(也就是废话)
今年年底,额,不对,应该说是去年了,我开始进行了一个多月的面试之旅。
面试的公司并不多,但从体量上来看,基本算是一二三线的大厂都囊括了,其中还包括BAT,当然,最后我也是顺利的拿到了offer,虽然不是很理想,但我也挺满意的,毕竟对我这种一直向往大厂的人来说,能进大厂已经算是很好的一次职业跃迁。在这个过程中,我也积累了不少面试经验,并且跟很多朋友交流过,不少人都说让我写篇面经分享下经验(其中也就几个人)。
我这个人呢,别的优点不说,朋友的善意请求我基本都很少推辞的,既然他们都这么热切的请求了,那我也不负君期,舍弃一天假期来写篇实战面经分享下。
个人背景
好了,不说废话了,先介绍一下个人背景,本人是17年毕业的,算到去年年底的话大概是四年半的工作经验,我当时第一个面试的公司是广州这边的欢聚时代集团,也就是yy直播的前身,后来还面试了字节、Lazada、腾讯、网易等公司,最后拿到欢聚和网易的offer,结果不算圆满吧,但怎么说呢,体验这么一圈下来,感觉大厂的面试难度也没想象中那么夸张,其中字节和Lazada我都是在技术终面被刷的,询问过内推的朋友,可能未必是技术方面的原因。当然,这是后话了,我想说的是,就算是面试大厂,只要准备充足的话,还是可以从容应对的。
每一次面试完之后我都有做问题的记录和复盘,基于篇幅原因,本文只会分享欢聚的面经,其他公司的面经后面再给大家整理好了。
欢聚时代一面(1h)
先做下自我介绍,固定环节
面试官:既然你用Java语言,那我们先讲点Java基础的东西吧,你说下Java有哪些锁?
按照机制区分的话,Java中包含的锁可以分为公平锁和非公平锁、乐观锁和悲观锁,独占锁和共享锁,还有什么无锁,偏向锁,轻量锁,重量级锁之类的吧,比如Synchroized升级的过程中就涉及到这几种锁的转换,大概就这些吧
面试官:好,那你说下Synchroized的升级过程?
这个应该是基本的八股文了吧,不用多说
面试官:用过HashMap吧,它的存储结构是怎样的?
不用多说,八股文最常见的,1.8之前的版本就是数组加链表,1.8之后链表长度达到8的阈值转成红黑树,当然,回答的时候尽量不要这么简单,可以加多点料,比如说下为什么1.8之后这么转换,有什么优点之类的,充充面子,给面试官有个我们基础还可以的印象
面试官:Volatile用过吧 ,有什么作用
修饰变量,保证多线程下修改共享变量对其他线程可见;禁止指令重排序
面试官:Volatile是怎么保证可见性的,原理是什么
基于缓存一致协议(MESI)
面试官:线程池有哪些参数,有什么作用,运行过程如何
基础八股文,不多说
面试官:拒绝策略有哪些,你们用的是哪个
CallerRunsPolicy:线程池之外的线程直接调用run方法执行,除非线程池shutdown
AbortPolicy:直接丢弃任务,抛异常,默认策略
DiscardPolicy :丢弃任务,不报错
DiscardOldestPolicy:抛弃阻塞队列最早的那个任务,把最新的任务重新放到队列中
面试官:有哪些阻塞队列,可以不断加任务的(SynchronousQueue),如果用SynchronousQueue,有两个任务进来,哪个能进队列,还是都不行
取决于线程池情况,这个队列不保留任务的,有任务被提交就直接转发给线程或创建新线程执行,如果最大线程数满了,不会进队列
**面试官:线程池如果线程空闲,哪个参数可以设置多久销毁 **
keepAliveTime
面试官:看到你简历上项目用了mq啊,为什么用RocketMQ,不用其他,说下选型原因
上家公司的项目是电商系统,从性能和业务上综合考虑,RocketMQ比较适合,也足够用,Kafka的话虽然性能更高,但功能较为单一。总的来说,在性能满足需求的情况下,大数据、日志处理场景基本选用Kafka,业务处理相关选择RocketMQ
面试官:你们项目用的是哪个垃圾收集器?
CMS
面试官:为什么用CMS,不用G1?
历史原因,一开始创建项目用的就是CMS,团队人员也很少G1方面的调优经验
面试官:CMS有什么缺点你知道吗?
对CPU敏感,如果CPU数量少,吞吐量会下降很多;
无法处理浮动垃圾,并发清除阶段用户线程还在执行,可能会产生浮动垃圾,达到某种程度就会出现"Concurrent Mode Failure",然后导致了一次full gc的产生
面试官:针对CMS产生的浮动垃圾,有什么办法
可以设置一个参数-XX:CMSFullGCsBeforeCompaction,表示CMS还要再执行多少次full GC才会做压缩,默认是0就可以。
面试官:什么时候会发生老年代的GC?
老年代发生垃圾回收说明触发了FULL GC,一般是老年代空间不足或者调用System.gc出发
面试官:你有没有遇到过线上的一些故障什么的,举个例子
排除网络断电等抽风的情况,一般线上问题都是业务代码出现问题或者访问量过高导致的,检查的方式也比较通用,就是经典的top命令查看异常的线程,然后再根据是否是VM Thread决定用jstack
还是jstat
命令作进一步的排查
面试官:说下MySQL吧,你了解它的索引结构吗?
数据库必问到的索引八股文,B+树
面试官:主键索引和非主键索引区别,索引覆盖怎么回事?
在innodb引擎中,非主键索引的叶子节点存放的是主键的值,而主键索引的叶子节点存放的是整行数据,如果没有发生索引覆盖的情况,根据非主键索引查到对应的数据还需要到主键索引做一次回表查询。
索引覆盖指的是一个索引包含了所有需要的字段,不需要回表查了
面试官:如果有个联合索引a,b,然后order a asc, b desc,会用到索引吗
8.0之后的版本可以,之前的版本不支持desc排序
面试官:你们是怎么做sql优化的?
一般来说,针对sql的优化都是在业务代码开发前就要做好的,除了基本的表字段设计,在开发的过程还要针对业务方法输出对应的sql语句,然后做好对应的sql review以及explain分析,同时还要考虑到业务的访问量,如果过高的情况下是否需要走强制索引之类的优化
面试官:你用过Redis做分布式锁,那你说说Redis分布式锁的核心要保证哪些因素?
加锁,解锁,给锁设置超时时间
面试官:分布式锁如果线程拿不到锁,直接就返回吗
不是,可以设置等待时间,比如Redisson就支持
面试官:分布式锁如果master挂了,然后锁没有同步到其他机器,这时别的线程也拿到锁了,怎么办
Redis做分布式并非绝对安全,最保底的方式就是保证业务幂等
面试官:如果锁即将过期,但业务没处理完,该怎么处理
可以参考Redission的看门狗设计,就是定时对即将失效的锁续期
面试官:幂等性和分布式锁是同时要的吗,为什么都有了幂等性还要加锁
分布式锁可以防止用户误操或者流量过高的情况,如果完全由业务幂等保底,可能会让流量都达到db(很好的问题)
面试官:你简历上写了缓存异常的处理方案,那你说说缓存穿透是什么样子,怎么处理,跟缓存雪崩有什么区别?
缓存穿透:指缓存和数据库中都没有的数据,这样每次请求都会去查库,不会查缓存,如果同一时间有大量请求进来的话,就会给数据库造成巨大的查询压力,甚至击垮db系统。
一般解决方案有两种:
1、缓存空对象,但设置一个较短的时间,避免占用大量内存
2、布隆过滤器
缓存雪崩:大量的key同一时间失效,导致流量都打到db
解决方案一般是设置给key设置随机过期时间,或者互斥锁的方式抵挡请求
面试官:项目有用到布隆过滤器吗,如果用的话要多大内存
没用到,说不上来,但也别这么直接,可以适当延伸一下,比如说经过综合考虑,项目场景在这方面不需要做那么复杂的功能,或者是运维层面做了大量的异常请求拦截之类的,别让面试官觉得你这方面没什么思考。
面试官:你简历上还写用了Spring,那你了解Spring AOP的原理吗?
常见的八股文,基于Java动态代理,简单描述下就好
面试官:如果一个类有两个方法加了注解,然后一个调用另一个方法,那个方法的注解会生效吗?
不会,因为同个类的话不是代理对象调用方法
面试官:说说项目方面吧,你简历上的领取优惠券有哪些流程,为什么用缓存还要接近100ms的rt
引入了RocketMQ异步入库的方式做削峰,防止流量过高的时候接口RT过高,至于为什么还接近100ms,是因为领取优惠券的逻辑一般是以券包方式,也就是同时领取多张优惠券,判断的逻辑比较多,所以在高峰期会耗时点。
面试官:你写了订单数据是以用户id维度分表,是基于什么考虑
业务场景需要满足用户可以查看自己订单
如果查的维度是其他字段,比如商品id,怎么办
可以把订单数据放es,或者定时任务将数据加到一张宽表
面试官:你们的项目每台机设置最大多少线程访问,有测过可以支撑多大的访问量吗
见鬼,刚好那时候脑子发昏不记得,所幸转换了个思路,表达我们项目最高cpu也就达到百分之30,说明还可以撑起至少一倍的访问量,我真聪明啊
印象中后面又问了几道项目方面的问题,然后就结束了面试,总的来说,第一面的问题难度不算高吧,虽然针对的点比较多,但也基本是常见的八股文,项目场景方面没什么考究,准备好的话还是很好面对的,不出意外,第二天hr那边就通知我一面通过,然后就约了二面的时间。
二面(1h)
一面隔了一个星期左右就开始第二轮面试,同样也是远程视频,不过跟第一面不同,这一面考察的大多跟项目有关,所以,在面试前对于项目方面的准备是必须下功夫的。
不用问,刚开始肯定是自我介绍了,介绍完面试官眉头紧锁了将近一分钟才开口.........
面试官:看你的简历,你在上家公司也呆了两年了,为什么要这个时候出来找机会
这点不用说啦,前爸爸不都说了吗,要么钱少了,要么受委屈了,当然面试的时候肯定不能这么轻浮,就按照常规的说法,比如说个人发展,职业规划什么的说下就好
面试官:你觉得现有的公司满足不了你了吗
是的,这也是我长久以来的考虑(废话,不然我闲的蛋疼来面试)
面试官:你说你看过jdk源码,你觉得哪个设计惊艳了你
这个因人而异,我就针对比较熟悉的AQS部分和HashMap索引设计那块做了讲解
面试官:缓存血崩和穿透该怎么做
一面就问过了,照着背就行
面试官:你简历上写了sql优化,那你是怎么针对索引做设计的
常规的八股文,尽量满足常用查询的字段走索引,然后多用explain做分析就可以规划大部分的情况。
后面的问题基本都是项目方面的细节了,比如优惠券怎么防刷,工作中你觉得有所成就的是哪一次,做了什么,这些问题没有参考答案,每个人的项目不同,工作经历也不同,只要针对自己的实际情况回答就可以。
不过,这个过程还是有几点值得跟大家分享下,就是分表迁移方面和高并发突发流量的问题。
因为笔者之前所负责的项目是电商系统,日活量达到百万级别,对订单那部分做了分表的处理,也算是技术上的亮点吧,简历上肯定是着重说明了,所以面试的时候也是一直被询问这两方面的场景。
分表的话呢,我们之前是把订单的数据从单表同步到100张新表,基于用户id做sharding key,迁移数据的过程,需要考虑到数据同步的一致性,以及不对现有的业务流程造成影响,所以我们采用的是订阅binlog的方式来迁移数据,算是比较常规的方案。
但怎么说呢,虽然方案很常见,但实际操作的时候你会发现需要考虑的点非常的多,比如历史数据和增量数据的同步问题,是停服还是不停服迁移,数据迁移一致后该怎么切换新系统,以及新系统上线后出现异常数据该怎么回滚回旧系统的问题等等,如果有同学做过分表迁移的话,这些方面的问题应该都是头疼过的。当然,你越头疼的点面试官就越是喜欢考究,所以,这方面的技术处理事先要考虑到各种可能性,你才能越有余力应付各种刁钻的面试题。
除此之外,笔者所从事的电商项目也包含了秒杀那部分的功能,虽然QPS不高,但也需要做好秒杀系统的常规处理,比如限流,高并发扣减库存,保证不超卖等。
以我个人的经验来看,处理秒杀的高并发场景无非两种方案,要么同步,要么异步,实际操作无非就是加机器或者是放到队列等待,当然,实际要考虑的点非常的多,在高流量下所有的业务缺陷都会被无限放大,你需要考虑各种异常的情况做好预防措施,还有补偿机制,限于篇幅,我无法在这里给大家讲解太多,而且我本人也并非对所有的场景有处理经验,只能介绍几点思路让大家去思考,
比如限流方面有什么策略,怎么防止大量的恶意请求;
扣减库存那里是直接扣db的库存数据,还是预减库存,如果是预减库存的话,Redis减了库存但db没减怎么办;
秒杀的过程有些服务如Redis挂了,该怎么处理;
流量突然增大,大到某个商品的访问量超过了Redis的单机阈值怎么办;
如果引入消息队列做削峰组件,那么消费过慢导致用户一直等待怎么处理等等。
虽说实际中我们基本没有机会遇到那么大的访问量,但你无法预防面试官是不是会考察,所以,如果你的项目中涉及到高并发场景的话,我是比较建议你在这些方面去下点功夫准备的。
接着说面试吧,问完了项目方面的东西,也基本没什么技术题了,剩下的算是一些软实力方面的问题,比如为什么写博客啊;博客访问量挺高的,是有专门运营过吗;还有未来有什么职业规划之类的,这些问题也是因人而异,虽说没什么难度,但事前准备的好点还是更利于你面试的过程表达流畅的,所以,也建议大家可以多准备下软实力方面的题。
结论:这一面八股文问的不多,项目方面问的比较多,软实力方面也略有涉及,总的来说,难度是有的,面试的流程也挺顺利,几天后就通知面试通过。
三面(2h)
三面的面试官是欢聚的技术总监,也是视频面试,这一面没问技术问题,更多像是在聊天一样,说实话,面试官是真能聊啊,整个面试过程长达两个小时,他一直在介绍欢聚时代的一些历史以及我面试岗位那个部门的业务情况,同时还对我未来的技术方向做了一些建议,让我受益匪浅。总的来说,这一面虽然在技术面试上没什么收获,但能遇到这么一位热心的面试官也算是我的运气了,这里不胜感激,可惜后面有更好的offer,我也没机会跟他做同事了。
hr面
hr面也没什么好说的,交流了我的职业情况和期待薪资,几天后给完流水就发了offer。
总结
整个面试过程就大概是这样了,我想对大家来说,看完文章有收获的可能是一面和二面的分享,总的来说,欢聚的技术面试难度不算很高,都是比较常规的面试考察点,我个人的话还是那句话,只要事先准备充足,还是可以从容应对的。
个人体会上的话也没什么好说的,毕竟整个过程比较顺利,没什么刁钻的题,后面我还会写几篇面经介绍下我面试其他公司遇到的难题,希望能帮到各位看官少遇点坑。好了,今天就这样了,我们下期再见。