本文来自作者投稿,原作者:yes
面试前的小姐姐
来说说前不久蚂蚁金服一面的情况。说来也是巧合,当时在群里有位蚂蚁金服的小姐姐发了个内推,看了下JD感觉可以试试于是就私聊了小姐姐发简历内推了。
我16年也就是大三上就开始实习了,到现在其实不到三年的经验。就我个人而言面试的经验真的是少之又少,不超过一个手掌的数。因此我简历发给小姐姐之后又联系她,让她晚点推让我先找几个公司练练手。
但是小姐姐说她可以先帮我热热身,她也是面试官(小姐姐可真的是太好了!),挑了晚上和小姐姐语音了20多分钟,虽然不是具体的面试内容,但是内容比这个更干!她我说了说一面二面大致的问的方向和注重点,还有一些注意事项:
一面:
小姐姐:上来先会给两道编程题,二选一,不会是很难的算法那种,比较务实。基本上常年在一线编程的话能写的出来。是发个链接给你的邮箱,然后点击链接在线编程,没有代码提示,写不出来基本上就是没了。
我:假如有些方法实在记不起来能在idea写了拷过去吗?
小姐姐:这个你可以跟面试官提,不过就我而言比较减分,并且你idea写代码,面试官可能会把代码拿来跑而不是目测过了就好了。如果没跑过也基本上没了。
我:哦哦好的好的。
小姐姐:然后就是自我介绍,一面着重考察基础方面,比如锁啊、GC啊、常见集合等等。再会根据你的简历,比如我看你简历写了dubbo,那就会问一些dubbo方面的问题。这么一堆谈下来差不多了,一面半小时左右。
我:哦哦好的好的。对了一面如果过了的话得多久才会有通知?周期会很长吗?
小姐姐:基本上2-3个工作日就会有结果了。
二面
小姐姐:二面的话比较考察项目,基本上会先让你描述一些项目整体的架构,从哪里到哪里数据怎么流转,项目的tps、qps等,基本上答不出来就认为你不是项目的核心人员。然后为什么这么设计,有哪些优化点?这是考察你对平日的工作有没有足够的思考。
我:哦哦好的好的(瑟瑟发抖)。
三面、四面
小姐姐:emm....你过了一面二面再说吧。
我:是是是!放眼当下。
其实我是真想先投几个公司先找下节奏的,但是小姐姐都这样说了我就只能直面人生了!过了两天面试官就来电话了,约个几天后的晚上7点,并且指明需要有电脑(小姐姐诚不我欺啊),我问需要摄像头不,答:不需要,能上网就行。
面试正文
其实我也忘记到底约的是7点还是7点半,反正我7点坐在电脑前面等了,内心激动的一批,饭都吃不下等到了7.35,终于等到你还好我没放弃!!
听到电话那头传来的温柔的小哥哥声音,来了他终于来了!我已经打开邮箱等待编程题的到来了!
小哥哥:来先做个自我介绍吧。
我:emm???不是说上来先做两道题吗?额可能是先自我介绍下再做题把?于是我就巴拉巴拉说了20秒,说到在现在公司做一部分前端开发的时候就被打断了。小哥哥:说说你写前端有什么感触?
我:我想要换工作有很大一部分原因就是因为现在我需要做前端,前端对我而言吸引力不大,虽然对于后端而言前端给予的正反馈更加的明显和及时,但是我更喜欢后端在背后默默输出的感觉。
小哥哥:是的,前端对于后端而言更加的简单,掌握了几个框架基本上就够用了,天花板比较低。
我:是的是的,后端的东西太多了,涉及到的面很广(各位前端听友,上面的话是小哥哥说的,原话,雨我无瓜。我也是身不由己是吧,请海涵哈)
小哥哥:看你简历写了Dubbo、Redis、RocketMq,你挑个你最厉害的说说?
我:(我擦,我个人觉得这个问题看似为我考虑,实则暗藏杀机)redis吧?
我其实大脑快速过滤了一下感觉redis比较稳,我觉得能问的无非是:redis为什么单线程?IO多路复用?redis和memcached的优缺点?redis五个对象?每个对象的底层数据结构?redis的布隆过滤器(我还可以给你引申个布谷鸟过滤器)、hyperLogLog(我还给你说出是基于伯努利实验) redis的过期删除机制?淘汰机制?redis的RDB和AOF?redis的事务?redis的主从?哨兵?集群?redis的分布式锁?redlock?(来嘛martin大神对刚redis作者的内容我都说的出来) redis的一些优化?大key拆分?通过游标分批返回避免key*等命令阻塞?禁止swap?考虑内存碎片等等?感觉好像挺稳的那就redis吧!
小哥哥:redis集群介绍一下
我:(来了来了!)redis的集群是将一共16384个槽分给集群中节点,每个节点通过gossip消息得知其它节点的信息,并在自身的clusterState中记录了所有的节点的信息和槽数组的分配情况
小哥哥:客户端是如何访问集群的?
我:客户端会先访问集群中的一个节点,如果槽命中直接访问,如果不命中,则会返回MOVED指令,并告知槽实际存在的节点,然后再去访问。(这里其实还有个迁移中的情况,如果访问的槽正在迁移,则返回ask命令,客户端会被引导去目标节点查找)
小哥哥:你们公司是用集群么?
我:是主从
小哥哥:知道关晓彤么?
我:???(什么情况这个跨度这么大吗?)
小哥哥紧接着说:就是和鹿晗那次把微博弄挂了的情况
我:哦哦哦知道知道
小哥哥:微博这么大的公司了,而且出了这么多这种事故?为什么还会挂了?
我:(我擦,为什么这么出了这么多次情况还会挂我咋知道!这么大公司监控服务很到位的啊,并且报警自动扩缩容这一套组合拳下来感觉不应该挂的啊,它为啥挂了...)我弱弱的说了句热点key的问题?太热了顶不住
小哥哥:那怎么处理呢?
我: 首先保障热点 key 过期问题,给不同的热点key分配随机的过期时间,保证过期的平滑。然后可以通过在 Redis 中设置分布式锁,只有获取到锁的请求才能够穿透到数据库,保证同一时间只有一个请求可以穿透到数据库更新缓存。
小哥哥:不是,我不是这个意思,我是问这个热点key的访问要如何解决?
我:可以通过 hash 分 key,把一个 key 拆分成多个 key,分布到不同的节点,防止单点过热。比如一个 key 之前就分到一个节点上,我把 key 做了拆分,就像一致性 hash 的虚拟节点,分散访问。
小哥哥:你说到一致性 hash?那和普通hash有什么区别?
我:一致性hash把hash的空间虚拟成一个圆环,key做hash落在圆环上,按顺时针查找,遇到的第一个缓存节点就命中。通过虚拟节点避免缓存分布不均,并且使得某个节点挂了之后,下面的节点只需要承担一部分的流量而不会因为需要承担所有流量而挂了,然后发生雪崩
小哥哥:好,那我们说回刚才的问题,你刚说的是一种解决方案那还有什么别的方案么?
我:(还有??我真的没了,我思考了十秒钟,一片空白不知道了)不知道了。
其实还有本地缓存,简单点就一个HashMap就行,或者Guava cache或者Ehcache。用本地缓存来应对极热数据。
小哥哥:那好吧,来说说MQ吧
我:(小哥哥有点失望的样子,哎也是不应该但是脑子就是想不起来..)常见的有RocketMQ、KafKa等,RocketMQ更适合业务,注重时延的优化。Kafka因为存在攒一波的思想,吞吐更高,并且适合大数据场景,不适合业务。
小哥哥:攒一波是什么意思?
我:攒一波就是发消息默认不是一条一条发,是等一波再发(其实攒一波思想很常见,例如tcp的纳格算法,pageCache的批量刷盘等等)
小哥哥:那Kafka是哪种模式?
我:什么意思?什么模式?
小哥哥:就是消息不是有推和拉两种模式
我:额...(我不知道,我就知道RocketMQ的,是基于拉的,虽说有个pushConsumer,但是本质上也就是拉,只是broker先hold住了拉的request。我也知道拉模式和推模式的优缺点,只是我当时傻了,我当时脑子想我简历就没写Kafka,为啥问我Kafka不问我RocketMQ,其实我应该先说说推拉模式的优缺点然后说了RocketMQ是采用什么的,然后说下我对Kafka不太熟,只是我当时傻了...)我说推?应该是推吧?不对不对好像是拉....(我真的是神操作)
小哥哥:不知道就说不知道,不要乱说一通。
我:是是是,我不知道对不起。(是的不知道就直接说不知道,然后应该把自己知道的说出来,弥补下)
小哥哥:说说synchronize和lock区别
我:(来了来了,赶紧弥补下刚才的操作)synchronized是java内置锁,不需要手动的解锁,支持可重入,但是非公平,不可中断,条件单一,在1.6之前性能较差,经过1.6优化只有性能有显著的提升。lock,基于AQS,拿reentrant为例,需要手动解锁,可重入,支持中断,支持多条件,支持超时操作。(来快问我synchronized底层和1.6做了什么优化,我来从monitor对象开始说到字节码到monitorenter和monitorexit再到mutex,从偏向锁到轻量级锁到重量级锁,我再说个逃逸分析、锁消除、锁粗化。再引申出个JMM,来个cpu缓存L1、L2、L3到MESI,我还可以给你来个cpu缓存行伪共享问题。什么你问的是锁为什么重?那就来上下文切换,内核态用户态,系统调用,再给你引到上一个mq话题,搞个mmap、sendfile零拷贝,扯一波pageCache、内存预分配、文件预热、mlock等。来啊!!!)
小哥哥:说说AQS原理吧
我:(擦竟然不问我synchronized)AQS主要是采用state,通过对state的CAS判断来获取锁和解锁,并且存在等待队列和条件等待队列来park相关线程之后入队等待,有公平和非公平两者模式来唤醒等待的线程。
小哥哥:那为什么需要个AQS?
我:主要是为了封装和抽象,通过封装了公共的方法,减少重复代码。
小哥哥:说说GC调优把
我:GC调优一般具体是通过GC日志的情况来分析。基本上发现minor gc频繁,新生代空间太小了。如果发现晋升的年龄很小,老年代迅速被填满,导致频繁的major gc,并且回收比率又很大,那说明对象的生命周期确实很短也需要调整新生代。如果看full gc很频繁,但是每次回收的内存就一点点,那目测就是内存泄露了。总体上就是根据分代的根本,也就是新生代朝生夕死的事实调整GC,避免分配大对象。具体还是得分析GC日志。
小哥哥:好,那说下mysql like有什么注意点?
我:最左匹配,防止全表扫描而不走索引
小哥哥:那说说mysql查找过程
我:就拿命中索引的说吧,innodb主键是聚簇索引,采用b+树结构,非叶节点存的是主键和指向子节点的指针,叶子节点存的就是整体行数据,整体都是有序的,通过主键扫描根据树查找,最终落到叶子节点,命中然后返回。(其实更细的有mysql的一页有16kb,一页其实有多行记录,命中一页之后还要通过行记录索引通过二分找到行记录)
小哥哥:那知道LSM树么?
我:(LSM,其实我是知道的,而且我还做过笔记,不过当时就是好耳熟啊...然后就没然后了)我说听过,但是不太记得
小哥哥:那cassandra知道么?
我:啥cass的?
小哥哥:就是cassandra
我:(我脑子在想什么卡丝娜的瑞,上面mq被教育过了)我果断的说不知道 (实际上我听过,只知道是个nosql数据库,没用过)
小哥哥:行,那说了这么多我们来写一道题把?
我:(我擦我才想起来还得写题,这不是先上来写题的吗,节奏怎么不对?) 好的好的
小哥哥:LRU知道吧?来实现这个接口
我:(哎我抖了个机灵....想展示一下自己知道的多),我可以用LinkedHashMap嘛,我继承LinkedHashMap,重写removeEldestEntry
小哥哥:不行,给你接口当然只能实现这个接口
我:哦哦好的。(好像弄巧成拙了),那我先说说思路吧,通过链表,存储节点,新插入的节点插入到头部,访问过的节点也移动到头部
小哥哥说:好的你写吧
我:我在草稿纸上画了一下,然后边写边说我的写什么,其实我感觉不说点啥有点奇怪,我就边写边说我在写啥。
我:大概过了10多分钟,我写完了。
小哥哥:看着我的代码,他也捋了一遍思路,口述一下说恩可以,那说说时间复杂度和空间复杂度把
我:我用了HashMap以空间换时间的思路存储了key和node之间的关系,并且有记录头尾节点的引用,并且链表是双向链表,因此插入和查找的时间复杂度都是O(1),空间复杂度是O(n)。
小哥哥:那说说要线程安全的话怎么改造吧
我:简单粗暴就是在每个方法上都加锁了,在竞争不是很激烈的时候挺合适的,再进阶一下,可以使用concurrentHashMap,然后再锁方法内部,移动链表等代码,减少锁的粒度。
小哥哥:行,那差不多了,你有什么想问的么?
我:我刚才的表现怎么样
小哥哥:从刚才的回答可以看出你有一定的积累,包括刚才你说的LinkedHashMap,可以看出你也有所准备的(emmm...好像让小哥哥觉得我能写出这个代码是因为我写过LRU...讲真我只写过继承LinkedHashMap的....从node实体都要自己建的开始我还真没写过..),不过还是需要多看看公众号,多看看一些好的博客,逛逛社区,至于今天的结果我答复不了,还是得会去讨论的。
我:是是是,持续学习,好的。
小哥哥:那今天就到这了。
我:嗯嗯好的好的
结果
说好的半小时左右,这一波下来算上笔试花了1小时25分钟.....结果挂了.... 回顾下刚才的情况,鹿晗那波没答出本地缓存、KafKa推拉都不知道、LSM树竟然不知道、cassandra也不晓得..推断出这个人好像知识面不是很广的样子...难受确实惭愧。
事后我去查了查鹿晗那波为挂了:主要是时间点在17年10.8号,17年的时候微博的报警扩缩容还是人为的,没有自动扩缩容,并且10.8号,国庆期间很多人出去玩了都没打开微博,然后还有很多平时不玩微博的吃瓜群众,一听到这个消息都打开微博,这波冷数据击垮了微博某个系统,导致雪崩。
KafKa 是拉,拉的时候没消息阻塞住,或者等消息达到一定数量拉请求才返回。
LSM(Log Structured Merge Trees),像日志一样顺序追加,顺序写,因此写入性能很高,为了优化读,到一定阶段会排序合并。
cassandra 上网查吧..这里不再赘述。
总而言之还是太菜了...
共勉。
推荐阅读
说说你对Binder驱动的了解?
你亲手写的代码,正在出卖你
深夜,聊聊设计模式编程·思维·职场
欢迎扫码关注