先来说说我自己情况吧,二本毕业,对自己的专业不怎么满意,感觉学历也不是很好,想转行干互联网,于是自己自学了半年Java,出来面试,只能说是一把辛酸泪啊,我自认为我学了这么久,应该至少能轻松通过一面,可只能说行业竞争太激烈,互联网寒冬可不是开玩笑的,失败率高居不下,相信也有很多应届毕业生也有跟我一样的遭遇吧,说实话,碰壁多了难免会自我怀疑,但是我没办法,既然自己选了这条路,中途放弃也不知道能去干什么,曾一度陷入很emo的状态,但是还是得继续找工作,继续学习啊,这里我也分享一下自己的面试经历吧,希望大伙能吸取点我失败的教训
前传
一面的面试官来到来我的跟前,开始对我的简历进行了一番打量然后就开始了技术提问。
面试官:什么是大事务?
我:就是 很大...的...事务??(说好的问基础,为啥我感觉一点也不基础呢?)
面试官: 看了下你在简历上边有写到过关于电商系统的设计,那我想深入问下你在电商系统设计的几个问题哈。
小林: 好的。
面试官: 你们电商系统的每天的日活量大概在多少呢?
小林: 嗯,日活用户数目在5万左右,搞促销活动的时候还会涉及到一些大流量的访问。
面试官: 嗯嗯,那么接下来我问你几个系统内部设计的场景吧。
小林: 嗯嗯。(表面风平浪静,内心还是会有些慌张)
面试官:你刚才提到了促销活动,那么在搞促销活动之前,你们应该会有一些特殊的准备吧,能和我讲几个场景的实际案例吗?
小林: 嗯嗯,我们的商品信息其实是存储在mysql里面的,当进行促销活动的时候需要进行一次预热工作,将一些热点数据加载到缓存层当中,减少对于实际mysql访问的压力。在缓存方面我之前一贯都是使用了redis来存储数据,但是高峰时期对于redis的查询依然是需要网络消耗,有些特殊的业务场景需要在循环里面对redis做查询(老旧代码的原因,不推荐在工作中着么使用),因此这部分的模块我加入了本地缓存作为优化手段。
面试官: 嗯嗯(就这??)
我停顿了一会,看面试官似乎还觉得说得不够,然后继续回答接下来的内容点。
我: 对于一些热点数据而言,我们的本地缓存使用的是Guava Cache 技术,它提供了一个LoadingCache接口供开发者进行缓存数据的本地管理。当查询数据不存在的时候会发生缓存命中失效,这时候可以通过定义内部的一些callable接口来实现对应的策略。
面试官:如果每次查询不了数据都需要在get的时候去重写策略,岂不是很麻烦吗?(其实面试官也用过这款技术,他就是故意深入问问求职者是否有更多的了解内部构造)
我:嗯嗯,其实可以在定义LoadingCache做一个全局性的callable回调操作处理,我脑海中还对这段代码有印象,主要是通过cacheloader来做实现。
面试官: 嗯嗯,那你对于这些缓存算法有过相关研究吗?可以讲讲自己的理解吗?
我: 嗯呢,常见的缓存队列可以采用lru算法,所谓的lru其实本质的核心就在于:最近最久未使用的数据,可能在将来也不会再次使用,因此当缓存空间满了之后就可以将其淘汰掉。简单的实现思路可以用一条队列来实现,当数组中的某个元素存在且再次被访问的时候就会将其挪到链表的首位,如果查询某些新元素发现在lru队列里面没有命中,则需要从db中查询然后插入到队列的首部。这样能够保持队列里面的元素大多数场景下都是热点元素,当队列的体积占满了之后,访问最低频率的元素就会从队尾被挤出。
面试官: 嗯嗯,可以再深入讲解下lru算法所存在的弊端吗?(内心仿佛在说,就这?
我: 嗯嗯,lru算法其实存在这缓存污染的问题,例如说某次批量查询操作,将一些平时用不到的数据也塞入到了队列中,将一些真正的热点数据给挤出了队列,造成缓存污染现象。因此后边就衍生出来了lru-k算法,其核心思想是给每个访问的元素都标识一个访问次数k值,这类算法需要多维护一条队列(暂且称之为访问队列),当数据的访问次数超过了k次之后,才会从原先的访问队列中转移到真正的lru队列里面。这样就能避免之前所说的缓存污染问题了,但是采用lru-k算法其实需要涉及到的算法复杂度,空间大小远高于前边提到的lru算法,这也是它的一个小”缺陷“吧。
面试官: 嗯嗯,好的,那关于缓存的回收策略你有了解过吗?
我: 嗯嗯,我在之前的工程中使用的guava-cache技术就是采用惰性回收策略的,当缓存的数据到达过期时间的时候不会去主动回收内存空间,而是在当程序有需要主动查询数据的时候才会去做内存回收的检测等相关操作。之所以不做主动回收的工作,我推测是因为自动回收程序的步骤对于cache自身需要维护的难度较高,所以改成了惰性回收策略,这一点和redis里的惰性回收策略有点类似,这种策略容易造成某些长期不使用的数据一直没法回收,占用了大量的内存空间。
面试官: 嗯嗯,好的,那么这个面试点先到此告一段落吧,我再问下你其他的业务场景。
我的内心渐渐恢复平静,一开始的那种焦虑和紧张感渐渐地消失了,又恢复了从前的那种淡定和从容。
我: 好的。
面试官: 你们的订单业务系统一般是怎么做分表操作的啊?可以介绍一下吗?
我:嗯嗯,可以的,我们的订单表每日的增加数目为5万条数据左右,一个月左右订单的数据量就会增加到100万条左右的数据,因此我们通常每个月都会按照月为单位来做分表操作。在用户的电商app界面上边有个订单查询模块,关于那块的业务我也做过相关的开发。
通常我的订单查询的数据都是按照时间顺序,先查询最近的数据,再查询之前的数据信息,结合前端做了分页涉及的功能,会根据前端传入的月份时间,来识别应该定位在哪张表进行查询。通常来说近三个月时间内的订单数据都是一些热点数据,所以我们将前三个月的数据存在同一张表里面专门做优化。
关于后续几个月的数据大多数情况下用户自身并不会涉及到查询功能,因此我们会定时将数据同步到es数据库里面,如果后续需要涉及这块的数据查询,则走es数据库。
面试官: 嗯嗯,那么你们的es和mysql之间是怎么做数据一致性的维护呢?
我: 我们借助的是阿里的一款开源中间件做数据同步,结合了canal+mysql+rocketmq来进行实现的。
canal会模拟成一台mysql的slave去接收mysql的master节点返回的binlog信息,然后将这些binlog数据解析成一个json字符串,再投递到mq当中。在rocketmq的接收端会做消息的监听,一旦有接收到消息就会写入到es中。
面试官: 嗯嗯,那么你能简单讲解下在这过程中遇到的困难吗?
我: 额,其实这一套环境在我入职的时候就已经搭建好来,我也只是大概知道有这么一个东西,具体的很多细节也并不是很熟悉....
(此时我的内心再一次流下了没有技术的眼泪.......)
面试官似乎有点失望,看了下项目,于是便切换了另一个问题进行询问。
面试官:好吧,你在平时的工作中有遇到过一些jvm调优相关的内容吗?
我:嗯嗯,有的。
面试官:哦,可以讲讲你是怎么去做jvm调优的吗?
我:我们一般都搞不定,遇到jvm栈溢出的时候重启并且增加机器就完事了。
面试官:...... 这确实是一种方案,能不能讲些有价值点的思路呢?
我:嗯嗯,我之前有学习了解到过,Java虚拟机将堆内存划分为新生代、老年代和永久代。
通常来讲,我们代年轻代对象会被存放在eden区域里面,这块区域当内存占比会相对于survivor区要大一些,大部分当对象在经历一次Minor GC之后都被销毁了,剩余当对象会被分入到survivor区里面,然后在survivor区进入新的垃圾回收,当回收当次数超过了阈值之后(默认是15次),对象就会从年轻代中晋升到老年代。当然如果说survivor区中相同年龄的对象体积大小之和大于了survivor区中一半的空间,那么此时对象也会直接晋升到老年代中。哦对了,jdk8之后还多出来了一个叫做元空间的概念。
我非常流畅地将自己对于jvm的理解讲了出来,感觉自己的这番回答似乎很满意。
面试官:嗯嗯,能深入介绍下吗你对于垃圾收集器使用方面的一些经验总结吗?
我:额....这块就不是很熟悉了
面试官: 好吧,那今天的面试就先这样告一段落吧。
我: 嗯嗯,我还有机会吗...
面试官: 我觉得你后边可以进步和提升的空间还有很大(意思是你太菜了),可以再学习学习哈。
听完此话后,我留下了没有技术的泪水....看来大概是凉了,唉,这个工作还是得继续找啊....
不过后来,我还是去问了下我在阿里工作的表哥(ps:当初决定转行Java也是有看到他高薪工作的因素),他在阿里干了五年后端开发,听到我现在的处境,也知道我只自学了半年Java,没有什么项目经验,所以帮我系统规划了一下,也帮我复盘了一下面试中的问题,不过鉴于他也是个大忙人,我也不好意思让他找时间专门来指导我,所以也没太麻烦他,不过我真得感谢我表哥,他帮我从阿里内部搞出来一份Java面试真题资料,然后又抽时间帮我系统整理了一下,他也说,这份资料自己好好啃啃,至少拿个offer找个好工作不成问题。这里我也不藏拙了,大家都是苦逼的打工人,这份资料我也免费分享出来,能帮到多少是多少,希望大家看完也能拿到好的offer
先是目录
Java基础
JVM
多线程
MySQL
Spring
Spring Boot
Spring Cloud
Dubbo
Mybatis
Redis
网络
Linux
MQ
Zookeeper
Netty
大数据
算法
项目
设计模式
完整版资料都在这收录好了,但是平台字数限制也发不出来全部答案,如有参加秋招求职或者跳槽加薪的朋友,或者想要入行Java的朋友收藏下来这份资料,关注私信我即可,最后希望所有参加秋招和毕业求职的朋友都能拿下心仪offer
内容精选
JVM
多线程
MySQL
Spring
Spring Cloud
MQ
大数据
算法
设计模式