呕心沥血之作--手把手教你面试
Hi,各位老铁们,刚刚拿到java后端大厂的offer,特来分享下面试心得,也是经过一个月的时间准备,再加上两个月的面试历程总结出来的,希望对各位老铁有所帮助,也祝愿各位老铁找到心仪工作。
关于自我介绍
自我介绍注意侧重点,有些需要一笔带过,有些需要着重描述,自我介绍也决定着后面面试官问你问题的大致方向,如果答得好,可以引导面试官向你熟悉的领域进行提问,总体时间把控下,自我介绍不超过两分钟。
1、首先是问好(礼貌很重要),简单自报姓名,学历以及工作经历,
例:面试官您好,我叫xxx,xx年本科毕业,xx年开始从事java开发工作,先后就职于xx科技、xx科技;
2、第二句是介绍自己做过哪些行业的项目或者产品,简单介绍下即可,面试官对你的业务不会很感兴趣,这里不要着重介绍业务。
例:工作以来,主要做过资产证券化、理财销售类金融产品、教育类产品中台和消费者售后服务数字化转型类的产品;
3、第三句是重点,要介绍当前的工作内容,比如目前带团队,有管理经验;推动落地一些优秀的方案;有过几十万QPS的场景方案设计;在公司因为做出什么业绩而获得过什么奖励等,如果这里答得不错,面试官可能会对你这里的方案或者一些重点业绩感兴趣,从而导致他后面会重点询问你相关技术点,所以这里是你扬长避短的最佳时机。
例:目前就职于XX公司,从开始参与系统模块的开发,到后来独立负责单个微服务,能够带领团队完成交付;期间因推动领域驱动设计落地,获得部门内部优秀代码质量奖;也曾作为主力开发,参与产品的一个流量峰值达50万QPS的技术方案建设
4、简单介绍你掌握的核心技术栈,先说你熟悉的核心技术栈,再说一些能够加分的,比如作为java后端开发,对前端也有熟悉,或者会其他脚本语言等,都可以说一下,然后结束自我介绍。
例:掌握技术栈主要有spring全家桶相关,redis、kafka、rocketMQ;数据库有mysql、oracle、ES等;前端react、vue也有了解
最后感谢下面试官,表示自己回答完毕。
关于项目细节
基于前面的自我介绍,面试官可能会对你的项目感兴趣,从而询问你的项目细节,这里千万不可以不清楚,如果你答不出来,面试官会认为你前面所说的方案设计是吹牛逼的~~
如果项目都是你深度参与的,相信关于业务上的,和一些代码实现细节,你应该是清楚的,我也不多说,每个人的历程不一样,我想提醒你的是,要对项目的整体有一个了解;比如我们前面所说的50万QPS的场景,面试官可能会对你的集群规模,数据库规模等感兴趣,以及你方案设计的细节,在承载几十万QPS,你的想法都是怎样的,所以我们需要知道的是:
1、并发量及数据量;假设业务核心写接口日常QPS在50左右,日活跃时间10小时,那么核心表每天数据量应该有50x3600x10 = 180W,月数据量就有180Wx30 = 5400W,年数据量预计达5400Wx12 = 6.48亿;
2、数据库集群规模;这么多数据,你的数据库规模有多少节点的分库,假设你有150个主节点的分库,那么一年下来,每个库大约会有6.48亿/150 = 432W数据;
3、应用服务器规模;如果查询有50万QPS的场景,应用服务器大约需要多少集群,怎么部署这些需要考虑;假设每个应用节点可以抗500QPS,那你得有1000个应用服务器集群,集群怎么部署,怎么扩容这些需要考虑清楚,数据也都要能对的上,这样才算对整个服务整体有一个认知。
当然面试官还有可能会问你方案设计的细节,前面所说的集群规模等于是硬件设施,但是在代码层面如何承载这么高的并发,方案的细节也是需要我们深度去考虑的,比如多级缓存,或者异步削峰等等,至于方案细节,我想你应该要有所思考。
如果你上面回答的很完美,面试官这时候对你会比较满意,接下来可能会问你一些技术原理,或者对于一些场景题的思考,下面我整理一些重点可能会询问的技术点;
关于三高系统设计思路(高并发、高可用、高性能)
关于如何设计三高系统的问题,老生常谈了,细节网上很多帖子,我也就不详细说了,但是面试还是要提炼一下,简单说一下的
从一个优秀的开发角度看:
1、代码合理分层,接口设计职责单一,例如领域驱动设计等
2、合理利用缓存、利用多线程做业务逻辑并行等加速代码执行速度
3、简化sql,及写出高性能的sql语句,例如避免深度分页,跨库查询,索引不合理等
4、对接口进行压力测试,分析出接口未来的性能瓶颈,以便在流量偶发增高时,提前预防;
从一个运维的角度看:
1、运维主要基于提前预警、出现故障能快速定位及快速恢复角度考虑
2、给系统配置流控、服务监控、超过阈值短信通知,出现异常可根据调用链,日志链路追踪快速定位问题原因,按需提前扩容等
3、若部分服务已经出现响应非常慢导致服务基本不可用,用jstack pid打印线程堆栈,jmap -dump:file=test.hprof,format=b 3307 生成dump文件,便于事后分析,然后及时扩容重启
从一个架构师的角度看(ps:虽然没有架构师的水平,但是有架构师的梦想,开发也要多从全局考虑):
1、首先我们需要保护的是数据库,数据库是整个服务最脆弱的环节,数据存储最好有序,避免随机读随机写等;如分库分表下的全局ID有序等方案设计;也可利用NOsql作为数据库的补充,例如需要全文检索的场景,可以利用ES查询;根据数据量及QPS等参数,对数据库进行合理的集群部署,如主从分离、分库分表等方案设计;数据还需要考虑容灾、备份等情况
2、应用服务的代码执行越快越好;池化技术,如线程池,数据库连接池,合理利用线程和IO资源,降低损耗;利用缓存加速代码执行,如果并发高,还可以考虑引入多级缓存;热点数据预热,静态资源通过CDN技术加载等
3、利用消息队列削峰
4、服务化治理,链路追踪,请求负载均衡,熔断机制等保证服务的基本可用(平衡加权轮询算法、加权随机算法、一致性哈希算法、最小活跃数算法);流量控制,网关层面做限流保护后端服务(漏桶算法、令牌桶算法等限流算法)
5、监控,通过监控及时发现系统的健康度,搭建ELK等日志追踪,服务器cpu、io、gc等情况监控
6、快速恢复,系统自愈等;全链路压力测试,分析系统性能瓶颈,一般数据库扩容困难,系统设计应该将流量尽可能挡在上层应用,从而方便系统动态扩容;熔断降级,保证核心流程基本可用
当然,关于三高设计,实际上远没有以上说的这么简单,毕竟理论和实践还有很远的路程,但是面试时间短,不可能面面俱到,这里只是我提炼的一些回答,各位老铁也可以自己总结,当然如果想要对于三高设计有详细的学习,也可以看看我的博客专栏《高并发系统设计》
关于数据库问题(以mysql为例)
1、事务四大特性ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
2、事务的隔离级别:
1)未提交读(会读到其他事务未提交的数据,视为脏读)
2)提交读(在当前事务中,会出现多次读的数据不一样,因为期间别的事务提交了,视为幻读)
3)可重复读(在当前事务开启后,多次读取的数据是一样的,因此解决的幻读问题,实现原理和mvcc有关)
4)串行化读(最高隔离级别,强制事务串行,性能最低)
3、mvcc:
1)redo log(重做日志):提交事务之后,mysql会把数据先存到Boffer Pool(缓冲池)里头,生成reedo log文件,然后后台线程完成日志文件到磁盘的持久化 (redo buffer)
2)undo log(回滚日志):用于记录数据被修改前的信息,用于回滚事务 (undo buffer)
3)快照读:读取的是快照版本,也就是历史版本,MVCC读取的是快照中的数据,可以减少加锁带来的开销。select是快照读;
4)当前读:读的是最新数据,需要加间隙锁。select ... for update, update、delete、insert都是当前读
4、覆盖索引(普通索引和组合索引很好理解,也都很简单,就不说了):
1)select count(name) from table; name加索引,无需回表
2)select name from table where id< 80000; id索引,需要回表查询, 组合索引(id,name)无需回表
3)select name from table order by id limit 50000,10; id索引,全表扫描; 组合索引(id,name)走索引排序,无需回表
改写:select name from table a inner join (select id from table order by id limit 50000,10) b on a.id=b.id
给id建索引,利用索引消除排序,但是需要回表 组合索引(id,name)走索引消除排序,无需回表
同样的,这里只是面试经验的总结,不讨论技术细节,关于mysql的一些优化细节,索引优化等可以自行学习。
关于JVM
JVM也是老生常谈的问题,高频面试题,网上也有很多详细介绍的博客,也可以自行看书学习,我这里提炼一下面试重点:
1、jvm内存模型:线程栈,计数器,堆,方法栈,本地方法区
2、堆:分为年轻代和老年代,年轻代(eden区和两个Survivor区)内存分配8:1:1,内存利用率90%
3、线程栈:线程私有空间,执行方法也就是压栈,执行完出栈,压入栈的是栈帧,比如代码块{}可以理解为一个栈帧;如果大量压栈不出栈,比如调用栈非常深,死循环调用方法,栈空间不足,抛异常StackOverflowError
4、方法区:它主要存放一些虚拟机加载的类信息,常量,静态变量,即使编译器后的代码等数据,内存不足抛OutOfMemoryError
5、jvm内存模型细节点:
1)工作内存(线程私有空间)独立运行,互不感知,程序在工作内存执行,结果共同写入主内存
2)线程安全:只能通过synchronized、ReentrantLock保证线程安全
3)volatile:每次线程用到volatile修饰的变量,会从主存加载最新值,修改后立即回写主存,保证线程之间可见
6、GC:年轻代GC采用复制算法,老年代采用标记整理算法;创建对象,分配内存,eden内存不足,触发MinorGC;fullGC前触发MinorGC;老年代空间不足,内存担保失败,晋升老年代时空间不足,主动system.gc()触发fullGC
关于spring
spring想必也是重点,网上关于spring的生命周期,循环依赖等帖子也是不胜枚举,我就不细说了,大家自行学习了解,我给一个提示:
生命周期加载过程:
1、工程启动从refresh()方法开始
2、DefaultListableBeanFactory#preInstantiateSingletons() 加载bean的方法
3、DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) 解决循环依赖的关键代码(三级缓存的缓存容器定义也在这个类)
关于线程池:
1、newCachedThreadPool:
a、因maximumPoolSize = Integer.MAX_VALUE,取名不定长线程池
b、用的SynchronousQueue队列是同步无界阻塞队列,默认非公平模式
c、公平模式就用队列,非公平模式用栈:fair ? new TransferQueue<E>() : new TransferStack<E>();
2、newFixedThreadPool:
a、因线程数是初始化传入的固定值,所以名为定长线程池
b、用的是LinkedBlockingQueue无界阻塞队列
3、newSingleThreadExecutor:单例线程的线程池,LinkedBlockingQueue无界阻塞队列
4、newScheduledThreadPool:定时调度线程池,DelayedWorkQueue工作优先级队列或延迟队列
5、newWorkStealingPool:工作窃取池,不公平,执行顺序不保证
JAVA基础篇
1、ArrayList和LinkedList区别:
a、底层数据结构不同,ArrayList底层是基于数组实现的,LinkedList底层是基于链表实现的
b、ArrayList更适合随机查找,LinkedList更适合删除和添加,查询、添加、删除的时间复杂度不同
c、ArrayList和LinkedList都实现了List接⼝,但是LinkedList还额外实现了Deque接口,所以LinkedList可以当做队列用
2、HashMap:
a、根据Key通过哈希算法与与运算得出数组下标
b、如果数组下标位置元素为空,则将key和value封装为Entry对象(JDK1.7中是Entry对象,JDK1.8中是Node对象)并放入该位置
c、如果数组下标位置元素不为空,则要分情况讨论
1)如果是JDK1.7,则先判断是否需要扩容,如果要扩容就进行扩容,如果不用扩容就生成Entry对象,并用头插法添加到当前位置的链表中
2)如果是JDK1.8,则会先判断当前位置上的Node的类型,看是红黑树Node,还是链表Node
如果是红黑树Node,则将key和value封装为红黑树节点并添加进去,在这个过程中会判断红黑树中是否存在当前key,如果存在则更新value
如果此位置上的Node对象是链表节点,则将key和value封装为链表Node并通过尾插法插入,尾插需要便利节点,顺便判断key是否存在,存在就更新
插入节点后,判断节点长度,大于等于8,转成红黑树,最后判断是否需要扩容
3、ConcurrentHashMap:
a、1.7是分段锁segment
b、1.8是cas来保证table线程安全,对于table下的链表或者红黑树,用synchronized
4、ThreadLocal:
a、ThreadLocal是Java中所提供的线程本地存储机制,可以利⽤该机制将数据缓存在某个线程内部,
该线程可以在任意时刻、任意方法中获取缓存的数据
b、Thread 类里面有一个ThreadLocal.ThreadLocalMap对象的引用,ThreadLocalMap引用Entry[]对象,Entry的key又是ThreadLocal,
如:Entry(ThreadLocal<?> k, Object v),当ThreadLocal没有被引用,从而GC了,但是因为线程存在而导致Entry里面的Object一直存在内存中
以上就是我总结的一些面试心得,当然面试题远不止于此,这里只是一个提炼和总结,不介绍技术细节,便于你面试的时候组织好自己的语言,做出完美回答,另外像缓存数据一致性问题、redis分布式锁,红锁等问题,zookeeper和eurka的区别等等,这些高频面试题,也是需要知道的,除了这些八股文,实际上我们也需要理解里面原理,这样才能在工作中得心应手;祝老铁们都能找到满意的offer!!