面试两周怀疑人生,还是怀疑社会好呢?

某求职者java高强度面试两周怀疑人生

  • 阿里云二面挂
  • 滴滴一面挂
  • 阿里健康一面挂
  • 天眼查一面挂
  • 瓜子二手车二面通过没消息
  • 网易有道一面挂
  • 美团一面挂
  • 度小满一面挂
  • 字节一面挂
  • 天眼查一面挂
    高德二面挂
    我觉得我答得都还可以的都没过
    每次面试下来总结,面试也答出来了,为什么就给挂了呢?

现在本科两年面试都问让你设计一个缓存,设计一个灰度系统,设计各种各样的系统。没经验啊,怎么办啊,现在大厂机会都用差不多了,也没个满意的offer,感觉处处都在和自己作对。
对找工作没信心了

一. 缓存架构设计(Redis)

Redis知识体系内容

1:缓存技术和框架的重要性

互联网的一些高并发,高性能的项目和系统中,缓存技术是起着功不可没的作用。缓存不仅仅是key-value的简单存取,它在具体的业务场景中,还是很复杂的,需要很强的架构设计能力。我曾经就遇到过因为缓存架构设计不到位,导致了系统崩溃的案例。

2:缓存的技术方案分类

  • 1)是做实时性比较高的那块数据,比如说库存,销量之类的这种数据,我们采取的实时的缓存+数据库双写的技术方案,双写一致性保障的方案。
  • 2)是做实时性要求不高的数据,比如说商品的基本信息,等等,我们采取的是三级缓存架构的技术方案,就是说由一个专门的数据生产的服务,去获取整个商品详情页需要的各种数据,经过处理后,将数据放入各级缓存中。

3:高并发以及高可用的复杂系统中的缓存架构都有哪些东西

1)在大型的缓存架构中,redis是最最基础的一层。高并发,缓存架构中除了redis,还有其他的组成部分,但是redis至关重要。

如果你的数据量不大(10G以内),单master就可以。redis持久化+备份方案+容灾方案+replication(主从+读写分离)+sentinal(哨兵集群,3个节点,高可用性)
如果你的数据量很大(1T+),采用redis cluster。多master分布式存储数据,水平扩容,自动进行master -> slave的主备切换。

2)最经典的缓存+数据库读写的模式,cache aside pattern。读的时候,先读缓存,缓存没有的话,那么就读数据库。

更新缓存分以下两种方式:
数据发生变化时,先更新缓存,然后再更新数据库。这种适用于缓存的值相对简单,和数据库的值一一对应,这样更新比较快。
数据发生变化时,先删除缓存,然后再更新数据库,读数据的时候再设置缓存。这种适用于缓存的值比较复杂的场景。比如可能更新了某个表的一个字段,然后其对应的缓存,是需要查询另外两个表的数据,并进行运算,才能计算出缓存最新的值的。这样更新缓存的代价是很高的。如果你频繁修改一个缓存涉及的多个表,那么这个缓存会被频繁的更新,频繁的更新缓存代价很高。而且这个缓存的值如果不是被频繁访问,就得不偿失了。
大部分情况下,建议适用删除更新的方式。其实删除缓存,而不是更新缓存,就是一个lazy计算的思想,不要每次都重新做复杂的计算,不管它会不会用到,而是让它到需要被使用的时候再重新计算。
举个例子,一个缓存涉及的表的字段,在1分钟内就修改了20次,或者是100次,那么缓存跟新20次,100次; 但是这个缓存在1分钟内就被读取了1次,有大量的冷数据。28黄金法则,20%的数据,占用了80%的访问量。实际上,如果你只是删除缓存的话,那么1分钟内,这个缓存不过就重新计算一次而已,开销大幅度降低。每次数据过来,就只是删除缓存,然后修改数据库,如果这个缓存,在1分钟内只是被访问了1次,那么只有那1次,缓存是要被重新计算的。

3)数据库与缓存双写不一致问题的解决方案

问题:并发请求的时候,数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改。另一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。
方案:数据库与缓存更新与读取操作进行异步串行化。(引入队列)
更新数据的时候,将相应操作发送到一个jvm内部的队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据的操作也发送到同一个jvm内部的队列中。队列消费者串行拿到对应的操作,然后一条一条的执行。这样的话,一个数据变更的操作,先执行删除缓存,然后再去更新数据库,但是还没完成更新。此时如果一个读请求过来,读到了空的缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。
这里有两个可以优化的点:
一个队列中,其实多个读缓存,更新缓存的请求串在一起是没意义的,而且如果读同一缓存的大量请求到来时,会依次进入队列等待,这样会导致队列最后一个的请求响应时间超时。因此可以做过滤,如果发现队列中已经有一个读缓存,更新缓存的请求了,那么就不用再放个新请求操作进去了,直接等待前面的更新操作请求完成即可。如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回; 如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值。
如果请求量特别大的时候,可以用多个队列,每个队列对应一个线程。每个请求来时可以根据请求的标识id进行hash路由进入到不同的队列。
最后,一定要做根据实际业务系统的运行情况,去进行一些压力测试,和模拟线上环境,去看看最繁忙的时候,内存队列可能会挤压多少更新操作,可能会导致最后一个更新操作对应的读请求,会hang多少时间,如果读请求在200ms返回,如果你计算过后,哪怕是最繁忙的时候,积压10个更新操作,最多等待200ms,那还可以的。如果一个内存队列可能积压的更新操作特别多,那么你就要加机器,让每个机器上部署的服务实例处理更少的数据,那么每个内存队列中积压的更新操作就会越少。其实根据之前的项目经验,一般来说数据的写频率是很低的,因此实际上正常来说,在队列中积压的更新操作应该是很少的。
举个例子:一秒就100个写操作。单台机器,20个内存队列,每个内存队列,可能就积压5个写操作,每个写操作性能测试后,一般在20ms左右就完成,那么针对每个内存队列中的数据的读请求,也就最多hang一会儿,200ms以内肯定能返回了。如果把写QPS扩大10倍,但是经过刚才的测算,就知道,单机支撑写QPS几百没问题,那么就扩容机器,扩容10倍的机器,10台机器,每个机器20个队列,200个队列。大部分的情况下,应该是这样的,大量的读请求过来,都是直接走缓存取到数据的,少量情况下,可能遇到读跟数据更新冲突的情况,如上所述,那么此时更新操作如果先入队列,之后可能会瞬间来了对这个数据大量的读请求,但是因为做了去重的优化,所以也就一个更新缓存的操作跟在它后面。

4)大型缓存全量更新问题的解决方案

问题:缓存数据很大时,可能导致redis的吞吐量就会急剧下降,网络耗费的资源大。如果不维度化,就导致多个维度的数据混合在一个缓存value中。而且不同维度的数据,可能更新的频率都大不一样。拿商品详情页来说,如果现在只是将1000个商品的分类批量调整了一下,但是如果商品分类的数据和商品本身的数据混杂在一起。那么可能导致需要将包括商品在内的大缓存value取出来,进行更新,再写回去,就会很坑爹,耗费大量的资源,redis压力也很大
方案:缓存维度化。举个例子:商品详情页分三个维度:商品维度,商品分类维度,商品店铺维度。将每个维度的数据都存一份,比如说商品维度的数据存一份,商品分类的数据存一份,商品店铺的数据存一份。那么在不同的维度数据更新的时候,只要去更新对应的维度就可以了。大大减轻了redis的压力。

5)通过多级缓存,达到高并发极致,同时给缓存架构最后的安全保护层。

亿级流量的商品架构图
图片说明

6)分布式并发缓存重建的冲突问题的解决方案

问题:假如数据在所有的缓存中都不存在了(LRU算法弄掉了),就需要重新查询数据写入缓存。对于分布式的重建缓存,在不同的机器上,不同的服务实例中,去做上面的事情,就会出现多个机器分布式重建去读取相同的数据,然后写入缓存中。
方案:分布式锁:如果你有多个机器在访问同一个共享资源,那么这个时候,如果你需要加个锁,让多个分布式的机器在访问共享资源的时候串行起来。分布式锁当然有很多种不同的实现方案,redis分布式锁,zookeeper分布式锁。
zookeeper分布式锁的解决并发冲突的方案
(1)变更缓存重建以及空缓存请求重建,更新redis之前,都需要先获取对应商品id的分布式锁
(2)拿到分布式锁之后,需要根据时间版本去比较一下,如果自己的版本新于redis中的版本,那么就更新,否则就不更新
(3)如果拿不到分布式锁,那么就等待,不断轮询等待,直到自己获取到分布式的锁

7)缓存冷启动的问题的解决方案

问题:新系统第一次上线,此时在缓存里可能是没有数据的。或者redis缓存全盘崩溃了,数据也丢了。导致所有请求打到了mysql。导致mysql直接挂掉。
方案:缓存预热。
提前给redis中灌入部分数据,再提供服务
肯定不可能将所有数据都写入redis,因为数据量太大了,第一耗费的时间太长了,第二根本redis容纳不下所有的数据,需要根据当天的具体访问情况,实时统计出访问频率较高的热数据,然后将访问频率较高的热数据写入redis中,肯定是热数据也比较多,我们也得多个服务并行读取数据去写,并行的分布式的缓存预热。

8)恐怖的缓存雪崩问题的解决方案

问题:缓存服务大量的资源全部耗费在访问redis和源服务无果,最后自己被拖死,无法提供服务。
方案:相对来说,考虑的比较完善的一套方案,分为事前,事中,事后三个层次去思考怎么来应对缓存雪崩的场景。
事前:高可用架构。主从架构,操作主节点,读写,数据同步到从节点,一旦主节点挂掉,从节点跟上。
事中:多级缓存。redis cluster已经彻底崩溃了,缓存服务实例的ehcache的缓存还能起到作用。
事后:redis数据可以恢复,做了备份,redis数据备份和恢复,redis重新启动起来。

9)缓存穿透问题的解决方案

问题:缓存中没有这样的数据,数据库中也没有这样的数据。由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。在流量大时,可能DB就挂掉了,要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞。
方案:有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法(我们采用的就是这种),如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。

二. 设计一个灰度系统

2.1 灰度系统概念

在飞速发展的互联网公司,灰度其实就是根据设定的规则将请求路由到我们的灰度版本(灰度机器)上来。比如对于API来说,一般有如下几个需求:特定用户(比如测试帐号)、 特定的App(比如测试app或者合作App)、特定的模块、接口(只有某些接口需要灰度,这种一般是API Container的修改,拿一些不是很重要的API做灰度测试)、特定的机器(某些请求IP转发到灰度机)等。

2.2 灰度发布能够解决的问题

我们公司是一个互联网公司,不过为了保障系统上线的可靠性,也是一样,每次系统的上线都是经过开发、site、test、bate、生产的多个过程。当程序员和测试人员信心满满的要将系统推到线上的时候,可能因为一个小小的配置导致系统的线上故障,风险不可预估。
每一次产品发布,交到用户手中的时候却不知道这次的产品设计是否能够给用户带来更好的体验,不能指定特殊人群,优先对新的产品进行线上的验证和体验。
基于如上的问题联想灰度可以解决的问题:

  1. 在发布过程中降低上线风险
  2. 降低影响范围,并且范围可控
  3. 降低对测试的依赖,减少线下自测的数据构造成本
  4. 特定的请求能够指向特定的服务器,方便集中监控日志,方便跟踪完整的调用链路
  5. 方便系统流量切入
  6. 方便回滚
  7. 指定特定人群,方便系统回访,方便产品需求收集,完善产品功能,提升产品质量
  8. 在无状态的情况下保障用户使用到的版本一致
  9. 避免停服给用户带来坏的体验用

2.3 思考

要能解决上面提到的问题有多重方式,不同公司有不同的实现方式。作为一个开源的灰度系统就要 考虑到对业务无侵入,方便升级和配置。
有很多公司实现灰度是基于多部署一套灰度环境作为灰度发布的场景,这样在某些层面是可以达到灰度的手段,但是前端入口配置杂乱,不易于维护,同时不能保障线上系统的配置和灰度系统相同,环境也可能存在差别,存在上线的风险。
开发一套灰度系统就变得尤为重要,尤其是对于小公司,没有精力去做也就无法保障系统上线的平稳。本人也是出于这种冲动,写下了一套灰度系统,将其开源出来,希望能够为小公司提供一种解决方案,希望能够作为大公司灰度更好参考。

2.4 灰度系统设计

本系管理统主要采用java技术开发,服务端主要用到了spring+springmvc+mybatis+dubbo,前端采用easyui;数据库采用mysql;灰度引擎当前采用java和lua两种技术开发。
项目代码:izhangll/灰度管理系统

2.4.1 设计目标

  1. 能够解决上面提到的问题
  2. 做到系统的灵活配置
  3. 做到系统不牵涉业务
  4. 做到对原有系统无侵入性

2.4.2 应有功能

图片说明

2.4.3 整体设计

图片说明

#学习路径#
全部评论
还可以投一下上海爱数哦,网申岗位及链接 https://www.aishu.cn/cn/campus-recruit 内推码:ZFM2002,免简历筛选
点赞 回复
分享
发布于 2021-09-26 12:30
可以试试投一下上海爱数呢,网申岗位及链接 https://www.aishu.cn/cn/campus-recruit 内推码:ZFM2002,免简历筛选
点赞 回复
分享
发布于 2021-09-26 12:31
阿里巴巴
校招火热招聘中
官网直投

相关推荐

最近春招在即,之前秋招在牛客上看到大家很多面经和资源,这里也分享下自己的秋招感悟,帮助大家更好的优化自己的简历,准备春招。楼主 末9本+30-50美硕,投后端开发方向。offer情况:北美大厂*2 + 国内大厂*3书接上文3. 面试面试这部分也是最重要的影响上岸的指标之一,首先说如何准备面试,其实无非就是算法题+八股+系统设计,leetcode就不用说了,硬刷就好,先把top100之类的过一遍,然后温故知新。八股的话,这个网站不错,我认为要是过1-2遍,糊弄80%的面试够用了(http://www.cyc2018.xyz/),系统设计的话楼主因为之前学过distributed system等,所以比较容易,建议有时间的同学可以看看MIT 6.824和 Alex Xu 的 system design的书。而面试时间的安排,对于海外党,给个建议,可以在简历上写一下“因为海外时差原因,只能在北京时间x点-y点接电话,万一有hr看到呢。而如果漏接了电话,如果能回拨直接回拨,不能的话去校招官网找联系邮箱提供个时间,一般都会回复和帮忙安排,别傻等着。4. offer选择首先谁all in任何公司我都笑谁,可能因为三方的缘故有的时候难当渣男,但尽量当,能海就海,不能海手里一定要留个offer。一般的公司总比失业好。其次,作为应届毕业生我个人的排序是 职业发展 >= 稳定性 >= 钱,当然如果某一项差太多了另当别论。然后compete offer的话就左脚踩右脚就好,表明自己手里的offer,要一个更高的价格,且表明比如如果不能给的更高现在的也能接受,但可能如果其他公司给的更好就去其他的之类的。目前想到的就写这么多,总的来讲任何招聘都是充满焦虑的,作为被挑选者,每个人都会焦虑,摆脱焦虑的最好方法就是move on,让自己忙起来就好,复习八股,刷题等,焦虑意味着上心了,离成功就不远了,祝各位春招上岸,拿到心仪公司的offer。
点赞 评论 收藏
转发
3 28 评论
分享
牛客网
牛客企业服务