化與高頻面經(jīng)實(shí)戰(zhàn)復(fù)盤)
看到標(biāo)題點(diǎn)進(jìn)來的朋友大概率和我之前一樣簡歷上寫著“兩年后端開發(fā)經(jīng)驗(yàn)”實(shí)際上每天的工作就是對著需求文檔寫接口、改接口增刪改查一條龍?jiān)傩抟恍蘧€上問題日復(fù)一日。我懂這種焦慮——不是不想學(xué)是每天CRUD已經(jīng)耗光了精力真要跳槽時(shí)又覺得自己“什么都不會”。這篇文章就是我自己從2年CRUD狀態(tài)準(zhǔn)備社招、最終上岸的全過程復(fù)盤。我會把簡歷怎么改、知識點(diǎn)怎么突擊、面試官到底在面什么以及我實(shí)際遇到的高頻面經(jīng)全部攤開來講給同樣處境的你一條可以直接照做的路線。文章不灌雞湯不繞彎子只講實(shí)操。適合1到3年后端經(jīng)驗(yàn)、正打算社招跳槽、又擔(dān)心自己基礎(chǔ)不夠扎實(shí)的朋友。我自己就是從這種狀態(tài)里走出來的所以我會盡量把“當(dāng)時(shí)我是怎么想的”“為什么這么準(zhǔn)備”“踩過哪些坑”都寫清楚讓你少走彎路。1. 先認(rèn)清現(xiàn)實(shí)兩年CRUD面試到底差在哪1.1 為什么“只會寫接口”會被面試官一眼看穿說句得罪人的話CRUD本身沒有錯(cuò)它是后端開發(fā)的起點(diǎn)但如果你兩年里只積累了CRUD面試時(shí)確實(shí)容易被問懵。我復(fù)盤自己第一次模擬面試時(shí)的狀態(tài)發(fā)現(xiàn)問題根本不在“代碼量不夠”而在三個(gè)能力維度上幾乎是空的第一是原理理解。平時(shí)寫接口SpringBoot自動(dòng)裝配把一切都準(zhǔn)備好了MyBatis把SQL映射好了Redis緩存工具類也封裝好了你只管調(diào)用。但面試官一問“SpringBoot自動(dòng)配置是怎么實(shí)現(xiàn)的”“MyBatis的二級緩存什么時(shí)候會失效”我當(dāng)場就是沉默。第二是方案設(shè)計(jì)。工作里需求來了就做很少思考“這個(gè)接口如果QPS上來會怎樣”“這個(gè)表數(shù)據(jù)量到千萬級怎么查”所以遇到系統(tǒng)設(shè)計(jì)題完全沒有思路。第三是問題排查。雖然也處理過線上Bug但基本靠日志和百度缺少系統(tǒng)的排查方法論。拿開車來類比你開了一輩子手動(dòng)擋車技確實(shí)沒問題但面試官問你“離合器工作原理是什么”“變速箱為什么這么設(shè)計(jì)”你答不上來他自然懷疑你是靠肌肉記憶在開車而不是真正懂這輛車。但這不意味著兩年CRUD就廢了。相反CRUD項(xiàng)目里藏著大量可挖掘的素材只是你平時(shí)沒把它當(dāng)回事。1.2 把CRUD經(jīng)驗(yàn)變成面試素材的三個(gè)步驟我當(dāng)時(shí)做了三件事把我那平平無奇的工作經(jīng)歷重新盤活了第一步把做過的項(xiàng)目全部列出來哪怕再小的模塊也寫下來。不要覺得“訂單查詢”“用戶管理”這種模塊拿不出手關(guān)鍵不是模塊本身而是你在模塊里遇到了什么、解決了什么。第二步針對每個(gè)項(xiàng)目問自己幾個(gè)“為什么”。接口慢過嗎為什么慢加了索引之后快了多少事務(wù)用在哪些地方有沒有出現(xiàn)過事務(wù)失效緩存和數(shù)據(jù)庫一致性怎么保證數(shù)據(jù)量大了分頁怎么辦不要急一個(gè)問題一個(gè)問題過。你會發(fā)現(xiàn)哪怕是一個(gè)簡單的訂單列表查詢都能延伸出索引優(yōu)化、分頁優(yōu)化、緩存設(shè)計(jì)、慢SQL排查等一系列話題。第三步按照“業(yè)務(wù)場景—技術(shù)方案—踩坑復(fù)盤”這個(gè)結(jié)構(gòu)把每個(gè)項(xiàng)目整理成三分鐘能講完的故事。面試官問項(xiàng)目時(shí)你不再說“我負(fù)責(zé)訂單模塊的開發(fā)”而是說“我當(dāng)時(shí)負(fù)責(zé)訂單查詢接口發(fā)現(xiàn)線上慢查詢通過分析執(zhí)行計(jì)劃定位到索引失效問題優(yōu)化后查詢耗時(shí)從800ms降到120ms”。差別是巨大的。1.3 建立面試官視角倒推準(zhǔn)備重點(diǎn)想明白面試官要什么準(zhǔn)備才不跑偏。社招面試官核心就看四件事基礎(chǔ)扎不扎實(shí)、項(xiàng)目真不真實(shí)、有沒有解決問題的經(jīng)驗(yàn)、潛力怎么樣。和校招不同社招更看重你“做過什么”以及“能不能立刻上手干活”。所以我把準(zhǔn)備時(shí)間的權(quán)重大概分配了一下Java基礎(chǔ)和JVM占30%MySQL和Redis占30%Spring生態(tài)占20%項(xiàng)目理解和業(yè)務(wù)場景占20%。算法單獨(dú)拎出來每天練不占用主線時(shí)間。很多人在準(zhǔn)備時(shí)容易陷入一個(gè)誤區(qū)瘋狂刷“面試寶典”把八股文背得滾瓜爛熟但一深問就露餡。面試官不傻背出來的答案和真正理解后的表達(dá)語氣、停頓、舉例方式完全不一樣。最好的準(zhǔn)備方式是每個(gè)知識點(diǎn)都能用自己的話講出來并且能對應(yīng)到某個(gè)具體場景。如果你能達(dá)到“聊到某個(gè)知識點(diǎn)時(shí)能自然聯(lián)想到自己項(xiàng)目里的某個(gè)坑”的程度那基本就穩(wěn)了。2. 社招簡歷怎么改把兩年CRUD寫出深度2.1 先刪掉這幾種簡歷廢話我看過很多同齡人的簡歷第一版幾乎都長一個(gè)樣子“熟悉Java、SpringBoot、MyBatis”“負(fù)責(zé)訂單模塊的開發(fā)與維護(hù)”“熟練使用MySQL、Redis”。這類描述最大的問題是沒有信息量?!笆煜ぁ钡降锥嗍臁柏?fù)責(zé)開發(fā)”到底做了什么面試官看了等于沒看只能靠面試時(shí)現(xiàn)場挖掘而現(xiàn)場挖掘往往兇多吉少。我建議你回去檢查一下自己的簡歷出現(xiàn)下面這幾類話直接改掉“負(fù)責(zé)XX模塊的開發(fā)與維護(hù)”改成“基于XX技術(shù)方案實(shí)現(xiàn)了XX功能解決了XX問題帶來了XX效果”?!笆煜ava、SpringBoot”改成“深入理解SpringBoot自動(dòng)配置原理熟悉Bean生命周期能在項(xiàng)目中獨(dú)立定位并解決循環(huán)依賴問題”?!笆炀毷褂肕ySQL”改成具體場景“主導(dǎo)過慢SQL優(yōu)化通過調(diào)整索引和改寫SQL將核心接口響應(yīng)時(shí)間從800ms優(yōu)化至120ms”。記住一個(gè)原則簡歷上每一句話都應(yīng)該讓面試官產(chǎn)生一個(gè)可以繼續(xù)追問的點(diǎn)而且你對這個(gè)點(diǎn)必須有真正的理解。寫上去的東西你都要能扛住三連問。2.2 項(xiàng)目描述的“場景—?jiǎng)幼鳌Y(jié)果”模板項(xiàng)目經(jīng)歷是社招簡歷的重頭戲。我推薦用“場景—?jiǎng)幼鳌Y(jié)果”這個(gè)結(jié)構(gòu)來寫也就是每一條都交代背景痛點(diǎn)、你做了什么、產(chǎn)出了什么。拿最常見的訂單系統(tǒng)舉例。普通寫法負(fù)責(zé)訂單列表查詢功能的開發(fā)與維護(hù)配合前端完成接口聯(lián)調(diào)解決線上問題。改后版本訂單模塊針對百萬級訂單數(shù)據(jù)查詢慢的問題基于EXPLAIN分析定位到索引失效原因通過優(yōu)化聯(lián)合索引和改寫SQL將列表查詢耗時(shí)從800ms降至120ms針對熱點(diǎn)訂單狀態(tài)查詢引入Redis緩存緩存命中率約85%有效降低數(shù)據(jù)庫壓力。有沒有感受到差別同樣一個(gè)功能后者立刻給面試官提供了幾個(gè)可以追問的點(diǎn)索引為什么會失效聯(lián)合索引怎么設(shè)計(jì)緩存和數(shù)據(jù)庫怎么保證一致性這些我在后面的面經(jīng)部分都會講到關(guān)鍵是簡歷里要把自己的“動(dòng)作”和“效果”具象化。再舉一個(gè)權(quán)限管理系統(tǒng)的例子權(quán)限模塊設(shè)計(jì)并實(shí)現(xiàn)了基于RBAC模型的用戶權(quán)限管理體系采用Spring Security JWT實(shí)現(xiàn)登錄認(rèn)證與接口鑒權(quán)通過自定義注解實(shí)現(xiàn)細(xì)粒度權(quán)限控制支持動(dòng)態(tài)刷新權(quán)限解決了權(quán)限變更需要重啟服務(wù)的問題。這個(gè)寫法的好處是面試官一看就知道你有一定的設(shè)計(jì)能力而且“自定義注解”“動(dòng)態(tài)刷新”這種點(diǎn)都很容易被追問正好給你展示的機(jī)會。還有一點(diǎn)項(xiàng)目描述盡量用數(shù)字說明。支撐的業(yè)務(wù)量、降低的耗時(shí)、提升的性能、覆蓋的用戶數(shù)都寫上。沒有精確數(shù)據(jù)怎么辦用相對值比如“優(yōu)化后耗時(shí)降低約60%”或者“接口TP99從1s下降到300ms”。只要是基于真實(shí)情況估算的都可以寫。2.3 簡歷投遞的節(jié)奏和渠道簡歷準(zhǔn)備好之后投遞節(jié)奏也很關(guān)鍵。我的經(jīng)驗(yàn)是先投不太想去的公司練手再投目標(biāo)公司。這樣做有兩個(gè)好處一是能通過真實(shí)面試檢驗(yàn)自己的準(zhǔn)備程度二是積累面試經(jīng)驗(yàn)避免在最想去的公司面前發(fā)揮失常。渠道方面主流招聘App回復(fù)率最高但要注意簡歷狀態(tài)設(shè)置為“在職狀態(tài)”避免被當(dāng)前公司看到。內(nèi)推比海投效率高很多可以找前同事、同學(xué)朋友幫忙也可以在一些技術(shù)社區(qū)找內(nèi)推貼。官網(wǎng)投遞適合目標(biāo)明確的大廠但流程可能偏慢。我個(gè)人不推薦一開始就投大量“保底公司”因?yàn)槊嬖嚭芟木η捌跍?zhǔn)備不足時(shí)容易連環(huán)翻車反而打擊自信心。投遞時(shí)間也有小技巧最好在工作日早上更新簡歷并投遞HR的活躍度最高。周末投的簡歷經(jīng)常被淹沒周一早上HR打開后臺可能已經(jīng)排了幾百條新消息。3. 高頻考點(diǎn)突擊后端社招面經(jīng)整理這一部分是我整理自己和其他上岸朋友面試中真正被問到的題目按主題分類每道題附上回答思路和考察點(diǎn)。不用全背但要確保每個(gè)主題的主干問題都能用自己的話講清楚。3.1 Java基礎(chǔ)與集合高頻必考點(diǎn)Java基礎(chǔ)是社招一面必考而且通常會先問集合因?yàn)榧夏馨褦?shù)據(jù)結(jié)構(gòu)、并發(fā)、源碼理解一次串起來。問題1HashMap的底層實(shí)現(xiàn)說一下回答思路先分版本。JDK1.7是數(shù)組鏈表JDK1.8是數(shù)組鏈表紅黑樹。put流程根據(jù)key的hashCode經(jīng)過擾動(dòng)函數(shù)計(jì)算hash值再通過(n-1)hash確定數(shù)組下標(biāo)如果該位置為空則直接放入否則鏈表尾插1.8鏈表長度超過8且數(shù)組長度大于等于64時(shí)轉(zhuǎn)為紅黑樹。擴(kuò)容是2倍擴(kuò)容負(fù)載因子默認(rèn)0.75觸發(fā)條件size threshold。為什么是2的冪次因?yàn)榭梢杂梦贿\(yùn)算替代取模同時(shí)擴(kuò)容時(shí)元素要么在原位置要么在原位置舊容量重排高效。面試官追問“為什么鏈表長度是8才轉(zhuǎn)紅黑樹”要答出泊松分布也就是在隨機(jī)hash下鏈表長度達(dá)到8的概率極低是時(shí)間和空間的平衡。問題2ConcurrentHashMap怎么保證線程安全回答思路1.7分段鎖1.8放棄分段鎖使用CASsynchronized鎖住數(shù)組桶的頭節(jié)點(diǎn)。put時(shí)如果桶為空就CAS插入否則synchronized鎖住頭節(jié)點(diǎn)進(jìn)行操作。size()通過baseCountCounterCell數(shù)組累加實(shí)現(xiàn)。重點(diǎn)強(qiáng)調(diào)JDK1.8的實(shí)現(xiàn)是“細(xì)粒度鎖”鎖的粒度更細(xì)并發(fā)度更高。問題3Synchronized和ReentrantLock的區(qū)別回答思路synchronized是JVM層面自動(dòng)加鎖釋放鎖ReentrantLock是API層面需要手動(dòng)加鎖解鎖ReentrantLock支持公平鎖支持中斷響應(yīng)支持多個(gè)條件變量Conditionsynchronized通過monitor實(shí)現(xiàn)JDK1.6之后有鎖升級過程無鎖→偏向鎖→輕量級鎖→重量級鎖。性能上兩者差距已經(jīng)不大選型一般看需求是否需要超時(shí)、中斷、公平鎖等高級功能。Java基礎(chǔ)里還有幾個(gè)容易被問的比如String為什么不可變、反射和動(dòng)態(tài)代理的應(yīng)用場景、泛型擦除機(jī)制。其中動(dòng)態(tài)代理幾乎必被問到因?yàn)樗苯雨P(guān)聯(lián)SpringAOP可以說是Spring面試題的入口。3.2 JVM與內(nèi)存必須會畫內(nèi)存圖和說GC流程JVM是社招中高級崗位的分水嶺考點(diǎn)即使初級崗位也會被問到基礎(chǔ)內(nèi)存模型。問題1JVM運(yùn)行時(shí)數(shù)據(jù)區(qū)域分為哪幾塊回答思路本地方法棧、虛擬機(jī)棧、堆、方法區(qū)、程序計(jì)數(shù)器。重點(diǎn)說堆它被線程共享存放對象實(shí)例又分為新生代Eden、S0、S1和老年代。虛擬機(jī)棧存放棧幀每個(gè)方法調(diào)用對應(yīng)一個(gè)棧幀棧幀里有局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口。方法區(qū)1.8之后就變成了元空間使用本地內(nèi)存。問題2對象什么時(shí)候被垃圾回收回答思路可達(dá)性分析算法從GC Roots出發(fā)不可達(dá)的對象被標(biāo)記。GC Roots包括虛擬機(jī)棧中引用的對象、方法區(qū)中靜態(tài)變量引用的對象、JNI引用的對象等。還要提引用計(jì)數(shù)法的缺陷循環(huán)引用。然后說垃圾收集算法復(fù)制算法新生代、標(biāo)記清除老年代、標(biāo)記整理老年代。CMS和G1的區(qū)別CMS并發(fā)標(biāo)記清除注重低停頓但會產(chǎn)生碎片G1是區(qū)域化分代收集可預(yù)測停頓時(shí)間JDK9之后成為默認(rèn)收集器。問題3線上CPU飆升或內(nèi)存溢出怎么排查回答思路這是一個(gè)典型的實(shí)戰(zhàn)題。CPU飆升top命令找到CPU高的進(jìn)程再top -Hp查線程jstack導(dǎo)出線程棧找RUNNABLE狀態(tài)的線程對應(yīng)的業(yè)務(wù)代碼。內(nèi)存溢出加-XX:HeapDumpOnOutOfMemoryError參數(shù)OOM時(shí)自動(dòng)dump堆文件用MAT或jvisualvm分析大對象。如果內(nèi)存溢出發(fā)生在元空間要查是不是CGLib動(dòng)態(tài)生成類太多。JVM知識點(diǎn)比較枯燥我的建議是不要死背找一臺測試機(jī)裝上JDK自帶工具實(shí)際跑一遍比如用jmap和jstack對一個(gè)簡單應(yīng)用做一次快照你會比死背書理解得透得多。3.3 并發(fā)編程能講出實(shí)戰(zhàn)場景才算過關(guān)并發(fā)編程考察頻率極高尤其喜歡問“你在項(xiàng)目中遇到過并發(fā)問題嗎”。問題1volatile的可見性和禁止重排序是怎么實(shí)現(xiàn)的回答思路volatile修飾的變量寫操作會立刻刷新到主內(nèi)存讀操作會從主內(nèi)存讀取這是通過內(nèi)存屏障實(shí)現(xiàn)的。它還禁止指令重排序所以可以用雙重檢查鎖的單例模式里修飾instance。要提volatile不能保證原子性比如i這種操作要用AtomicInteger或synchronized。問題2線程池參數(shù)怎么設(shè)置回答思路核心線程數(shù)、最大線程數(shù)、空閑存活時(shí)間、時(shí)間單位、阻塞隊(duì)列、線程工廠、拒絕策略?;卮饡r(shí)不要只說參數(shù)要說選擇依據(jù)。CPU密集型線程數(shù)設(shè)為CPU核心數(shù)1IO密集型設(shè)為CPU核心數(shù)*2或更多因?yàn)镮O等待時(shí)可以讓出CPU。還要說提交流程先核心線程滿了進(jìn)隊(duì)列隊(duì)列滿了創(chuàng)建新線程到最大線程數(shù)再滿走拒絕策略。四種拒絕策略要能說出來AbortPolicy拋異常、CallerRunsPolicy調(diào)用者執(zhí)行、DiscardPolicy丟棄、DiscardOldestPolicy丟棄最舊。問題3CAS的原理和ABA問題回答思路CAS比較并交換底層通過Unsafe類提供原子操作樂觀鎖思想。ABA問題一個(gè)值從A變成B又變成ACAS會認(rèn)為它沒有變化。解決方案是版本號AtomicStampedReference。日常開發(fā)中ABA問題很少遇到但面試官很喜歡拋出來看看你有沒有想過。并發(fā)部分我建議你結(jié)合項(xiàng)目準(zhǔn)備一個(gè)小案例比如“我在項(xiàng)目里用線程池CountDownLatch并行處理多個(gè)查詢把接口時(shí)間從2s優(yōu)化到800ms”這比單純背概念管用得多。3.4 Spring與SpringBoot最容易丟分的框架題框架題看似簡單但面試官會往深處問。問題1Spring Bean的生命周期回答思路實(shí)例化→屬性填充→初始化InitializingBean、init-method、BeanPostProcessor前置和后置處理→使用→銷毀。重點(diǎn)提BeanPostProcessorAOP動(dòng)態(tài)代理就是通過它實(shí)現(xiàn)的。這里可以把SpringAOP的原理一起說了動(dòng)態(tài)代理JDK代理和CGLIB代理前者要求實(shí)現(xiàn)接口后者通過繼承生成子類。問題2Spring怎么解決循環(huán)依賴回答思路三級緩存。一級緩存存成品Bean二級緩存存早期暴露的Bean還未完成屬性填充三級緩存存ObjectFactory用于生成代理對象。創(chuàng)建A時(shí)檢測到A依賴B先把自己放入三級緩存再去創(chuàng)建BB依賴A從三級緩存拿到A的早期引用完成屬性填充后放入二級緩存B創(chuàng)建完成后A再從二級緩存拿到完整引用最終放入一級緩存。面試官追問“為什么三級緩存不能省”要答出是為了處理AOP代理對象如果不提前暴露代理會導(dǎo)致注入的Bean不是最終的代理對象。問題3SpringBoot自動(dòng)配置原理回答思路SpringBootApplication包含EnableAutoConfiguration該注解通過Import導(dǎo)入AutoConfigurationImportSelector它會掃描META-INF/spring.factories文件中的自動(dòng)配置類按條件注解ConditionalOnClass、ConditionalOnMissingBean等判斷是否生效。這就是為什么我們引入依賴后不用配置SpringBoot自動(dòng)幫我們創(chuàng)建了需要的Bean。常見追問是“ConditionalOnMissingBean有什么作用”答允許用戶自定義覆蓋默認(rèn)配置。問題4Transactional事務(wù)失效的場景有哪些回答思路方法不是public的異常被catch吞掉自調(diào)用同類內(nèi)部方法調(diào)用不走代理異常類型不是RuntimeException時(shí)未配置rollbackFor數(shù)據(jù)庫引擎不支持事務(wù)類沒有被Spring管理沒加Controller/Service等。這些失效場景非常貼合實(shí)際項(xiàng)目如果你在簡歷里寫過“事務(wù)”相關(guān)的內(nèi)容這個(gè)題基本必問。3.5 MySQL與SQL優(yōu)化社招必考重頭戲MySQL在社招面試中的比重可能比Java還大因?yàn)楹蠖巳粘4蚪坏雷疃嗟木褪菙?shù)據(jù)庫。這一塊我建議重點(diǎn)準(zhǔn)備。問題1為什么InnoDB用B樹而不是B樹回答思路B樹非葉子節(jié)點(diǎn)不存數(shù)據(jù)只存索引所以每頁能存放更多索引項(xiàng)樹高更低一般3層就能存千萬級數(shù)據(jù)B樹葉子節(jié)點(diǎn)用雙向鏈表連接適合范圍查詢和排序B樹查詢穩(wěn)定所有數(shù)據(jù)都在葉子節(jié)點(diǎn)。哈希索引適合等值查詢但無法做范圍查詢。問題2聚簇索引和二級索引的區(qū)別回答思路聚簇索引葉子節(jié)點(diǎn)存整行數(shù)據(jù)一個(gè)表只能有一個(gè)聚簇索引一般是主鍵。二級索引葉子節(jié)點(diǎn)存主鍵值通過二級索引查詢需要回表查聚簇索引拿到完整數(shù)據(jù)。補(bǔ)充覆蓋索引如果查詢的字段都在二級索引里就不需要回表。問題3SQL優(yōu)化的整體思路回答思路先定位慢SQL開啟慢查詢?nèi)罩居肊XPLAIN分析執(zhí)行計(jì)劃看key是否命中索引、rows掃描行數(shù)、type類型systemconsteq_refrefrangeindexall。優(yōu)化手段包括加索引、改寫SQL避免索引失效比如左模糊查詢、隱式類型轉(zhuǎn)換、函數(shù)操作索引列、避免select *、大分頁優(yōu)化用延遲關(guān)聯(lián)或游標(biāo)分頁、減少不必要的事務(wù)。我舉個(gè)真實(shí)例子。我們有一個(gè)運(yùn)營后臺的列表頁每天上午10點(diǎn)必卡后來EXPLAIN一看發(fā)現(xiàn)狀態(tài)字段的區(qū)分度太低優(yōu)化器認(rèn)為走索引還不如全表掃描快于是改成了“創(chuàng)建時(shí)間狀態(tài)”的聯(lián)合索引問題立刻解決。這就是典型的區(qū)分度和最左前綴的應(yīng)用場景。問題4事務(wù)隔離級別和MVCC。回答思路讀未提交、讀已提交、可重復(fù)讀、串行化。MySQL默認(rèn)是可重復(fù)讀。MVCC通過隱藏字段DB_TRX_ID事務(wù)ID、DB_ROLL_PTR回滾指針和undo log實(shí)現(xiàn)在可重復(fù)讀級別下通過ReadView實(shí)現(xiàn)快照讀保證同一個(gè)事務(wù)內(nèi)多次讀取結(jié)果一致。當(dāng)前讀需要加鎖使用next-key鎖解決幻讀。3.6 Redis實(shí)戰(zhàn)緩存三板斧是大熱門Redis在后端面試中的重要性這幾年直線上升幾乎每場面試都會問到。問題1Redis都有哪些數(shù)據(jù)類型底層分別是什么回答思路StringSDS簡單動(dòng)態(tài)字符串、Hashziplist或hashtable、Listquicklist、Setintset或hashtable、ZSetskiplistziplist。補(bǔ)充一下1.8之后新類型Bitmap、HyperLogLog、Geo。通常再問一下每種類型的應(yīng)用場景String做緩存和計(jì)數(shù)器Hash存對象List做消息隊(duì)列或最新列表Set做去重和共同好友ZSet做排行榜。問題2緩存穿透、緩存擊穿、緩存雪崩的區(qū)別和解決方案回答思路穿透是查詢不存在的數(shù)據(jù)解決方案有布隆過濾器、緩存空值擊穿是熱點(diǎn)key過期瞬間大量請求打到數(shù)據(jù)庫解決方案是互斥鎖、邏輯過期、熱點(diǎn)key永不過期雪崩是大量key同時(shí)過期或Redis宕機(jī)解決方案是過期時(shí)間加隨機(jī)值、Redis高可用哨兵或集群、本地緩存兜底。這三個(gè)概念一定要區(qū)分清楚面試官還喜歡問“你們項(xiàng)目里怎么防止緩存穿透”我會答“接口層先做參數(shù)校驗(yàn)命中黑名單直接返回再用布隆過濾器攔截不存在的商品ID最后緩存空值兜底”。問題3緩存和數(shù)據(jù)庫一致性怎么保證回答思路先更新數(shù)據(jù)庫再刪除緩存。為什么不是先刪緩存再更新數(shù)據(jù)庫因?yàn)椴l(fā)下容易讀到舊值。為什么不是更新緩存因?yàn)閷戭l繁時(shí)緩存會被反復(fù)更新但讀不到浪費(fèi)資源。刪除緩存后如果刪除失敗怎么辦加一個(gè)重試機(jī)制或者訂閱binlog異步刪除。更穩(wěn)妥的是延遲雙刪先刪緩存、更新數(shù)據(jù)庫、等一小段時(shí)間再刪一次緩存這個(gè)“一小段時(shí)間”需要根據(jù)業(yè)務(wù)評估。注意一致性保證沒有銀彈面試官想聽的是你“理解各種方案取舍”而不是盲目追求強(qiáng)一致。問題4Redis分布式鎖怎么實(shí)現(xiàn)回答思路SETNX 過期時(shí)間更完整的版本是SET key value NX EX 30value用唯一標(biāo)識UUID釋放鎖時(shí)先判斷是不是自己的鎖再刪除用Lua腳本保證原子性。如果業(yè)務(wù)執(zhí)行超過鎖的過期時(shí)間怎么辦可以開啟一個(gè)看門狗線程續(xù)期Redisson的實(shí)現(xiàn)。存在主從切換導(dǎo)致的鎖丟失問題更嚴(yán)格場景可以用RedLock但RedLock本身也有爭議能說出優(yōu)缺點(diǎn)就行。3.7 消息隊(duì)列與分布式基礎(chǔ)了解常用方案如果你簡歷里寫過MQ相關(guān)經(jīng)驗(yàn)這部分要認(rèn)真準(zhǔn)備如果沒寫過至少要把基礎(chǔ)知識過一遍。問題1為什么用消息隊(duì)列回答思路解耦、異步、削峰。比如下單成功后要發(fā)短信、加積分、更新統(tǒng)計(jì)同步調(diào)用太慢且耦合引入MQ后只需要發(fā)送一條消息下游服務(wù)各自消費(fèi)。同時(shí)把高并發(fā)流量削峰保護(hù)下游系統(tǒng)。問題2引入MQ后可能遇到哪些問題回答思路消息丟失生產(chǎn)者的確認(rèn)機(jī)制、MQ的持久化、消費(fèi)者的手動(dòng)ack、消息重復(fù)消費(fèi)消費(fèi)端做冪等用唯一業(yè)務(wù)標(biāo)識去重、消息積壓排查消費(fèi)速度變慢的原因必要時(shí)臨時(shí)擴(kuò)容消費(fèi)者、消息順序性同一條業(yè)務(wù)消息進(jìn)同一個(gè)隊(duì)列順序消費(fèi)。問題3分布式事務(wù)了解嗎回答思路說Final一致性方案。本地消息表事務(wù)和寫消息在同一數(shù)據(jù)庫事務(wù)里然后通過任務(wù)調(diào)度投遞消息。TCCTry、Confirm、Cancel三個(gè)階段需要業(yè)務(wù)方實(shí)現(xiàn)對應(yīng)接口。RocketMQ事務(wù)消息半消息機(jī)制先發(fā)送半消息本地事務(wù)成功后commit否則rollback。不用說得太深但要能說出“分布式事務(wù)沒有銀彈能不用就不用盡量從架構(gòu)上避免跨庫事務(wù)”。3.8 算法與系統(tǒng)設(shè)計(jì)通過社招的硬門檻算法題對于工作幾年的人來說確實(shí)痛苦但必須過。我的準(zhǔn)備方案是LeetCode Hot 100 劍指Offer重點(diǎn)題按專題刷數(shù)組、鏈表、二叉樹、字符串、動(dòng)態(tài)規(guī)劃、棧隊(duì)列。如果時(shí)間緊優(yōu)先保證數(shù)組、鏈表、二叉樹的常見題能白板寫出來因?yàn)檫@幾類是面試最高頻的。動(dòng)態(tài)規(guī)劃至少掌握斐波那契、爬樓梯、最長公共子序列、背包問題這類模板題。系統(tǒng)設(shè)計(jì)題社招一般不會太難但很考驗(yàn)?zāi)阌袥]有“設(shè)計(jì)思維”。常見的有“設(shè)計(jì)一個(gè)短鏈接系統(tǒng)”“設(shè)計(jì)一個(gè)秒殺系統(tǒng)”“設(shè)計(jì)一個(gè)登錄認(rèn)證體系”?;卮鹂蚣芟瘸吻逍枨笤俟浪阋?guī)模然后設(shè)計(jì)數(shù)據(jù)模型和接口最后畫架構(gòu)并考慮擴(kuò)展性。平時(shí)可以拿自己的業(yè)務(wù)系統(tǒng)練手想想如果讓你從零設(shè)計(jì)現(xiàn)在的訂單系統(tǒng)你會怎么設(shè)計(jì)。4. 實(shí)戰(zhàn)復(fù)盤從一面到HR面的完整流程4.1 一面基礎(chǔ)扎實(shí)度篩選一面面試官通常是組里的工程師主要考察基礎(chǔ)知識和項(xiàng)目真實(shí)性。我的第一場一面記憶猶新先自我介紹了三分鐘然后面試官就直接問HashMap源碼接著問了ConcurrentHashMap、線程池參數(shù)、MySQL索引、Redis穿透。全程沒有問業(yè)務(wù)就是標(biāo)準(zhǔn)的面經(jīng)問題。這種一面其實(shí)相對簡單只要你按第三部分準(zhǔn)備到位正常發(fā)揮就能過。但要注意一點(diǎn)一面也會問項(xiàng)目。我當(dāng)時(shí)被問到“你的項(xiàng)目里Redis是怎么用的”我答了緩存熱點(diǎn)訂單查詢他又問“緩存和數(shù)據(jù)庫不一致怎么辦”。這提醒我們項(xiàng)目里寫過的每個(gè)技術(shù)點(diǎn)都要能預(yù)設(shè)至少三層追問。4.2 二面項(xiàng)目深挖與方案設(shè)計(jì)二面一般是組長或主管問題更開放喜歡讓你“講一個(gè)你印象最深的項(xiàng)目或Bug”。這時(shí)候前面整理的項(xiàng)目故事就派上用場了。講項(xiàng)目我推薦用這個(gè)框架背景是什么→我負(fù)責(zé)哪塊→遇到什么困難→怎么排查和解決的→最后結(jié)果如何→如果重做會有什么改進(jìn)。不要流水賬要突出“沖突”和“解決”。舉個(gè)例子我當(dāng)時(shí)講了一個(gè)線上慢SQL問題。背景是訂單列表頁每到促銷節(jié)點(diǎn)就特別慢我通過慢查詢?nèi)罩径ㄎ坏揭粭l查詢耗時(shí)3s的SQLEXPLAIN發(fā)現(xiàn)type是ALL全表掃描。原因是狀態(tài)字段單獨(dú)建了索引但區(qū)分度太低優(yōu)化器不走索引。我的解決方案是把索引改成“創(chuàng)建時(shí)間狀態(tài)”的聯(lián)合索引并且把分頁從limit offset改成游標(biāo)查詢。上線后接口從2s降到200ms。講完之后面試官追問了“為什么區(qū)分度低不走索引”和“游標(biāo)查詢的優(yōu)缺點(diǎn)”這兩個(gè)我都有準(zhǔn)備所以節(jié)奏很順利。二面還可能出一道簡化的系統(tǒng)設(shè)計(jì)題。我當(dāng)時(shí)被問“如果讓你設(shè)計(jì)一個(gè)秒殺系統(tǒng)你怎么做”。我就不慌了按“前端答題—網(wǎng)關(guān)限流—Redis預(yù)減庫存—MQ異步下單—數(shù)據(jù)庫最終扣減”這個(gè)思路講面試官還問“超賣怎么解決”我答“Redis執(zhí)行扣減用Lua腳本保證原子性數(shù)據(jù)庫更新用stock 0條件防止超賣”。能講到這個(gè)深度二面基本穩(wěn)了。4.3 三面/交叉面和HR面軟素質(zhì)與穩(wěn)定性到了三面或者HR面基本就是看人問一些“為什么離職”“未來幾年規(guī)劃”“遇到過最大的挑戰(zhàn)”之類的問題。我的經(jīng)驗(yàn)是離職原因一定不要說前東家壞話統(tǒng)一口徑是“希望接觸更大的技術(shù)挑戰(zhàn)和平臺”。職業(yè)規(guī)劃不要說“我想轉(zhuǎn)管理”或“我想躺平”說“希望在技術(shù)方向深耕成為某個(gè)領(lǐng)域的專家”比較安全。HR面還有一個(gè)容易被忽略但很重要的點(diǎn)談薪。社招談薪是有空間可談的比如固定14薪還是根據(jù)績效浮動(dòng)、試用期打折不打折、加班費(fèi)怎么算、公積金基數(shù)是多少。這些不問清楚入職后很容易有落差感。我當(dāng)時(shí)就是在一家公司的HR面前不好意思問得太多結(jié)果發(fā)現(xiàn)年終獎(jiǎng)是“依據(jù)績效浮動(dòng)”而不是“約定一定發(fā)放”這點(diǎn)在offer溝通時(shí)需要特別確認(rèn)清楚。4.4 面試過程中的節(jié)奏管理整個(gè)跳槽周期建議控制在1到2個(gè)月。太短準(zhǔn)備不充分太長容易疲憊。我的節(jié)奏是前兩周集中復(fù)習(xí)基礎(chǔ)知識第三周開始投簡歷每周保持2到3場面試的頻率每場面試結(jié)束當(dāng)天晚上做復(fù)盤。復(fù)盤不是記錄“問了什么”而是反思“哪道題答得不好、為什么不好、下次怎么答”。還有一個(gè)別人沒提到的細(xì)節(jié)面試前把上一場面試沒答上來的題立刻查資料吃透。因?yàn)橥粋€(gè)知識體系在多個(gè)公司之間是高度重疊的第一場被問倒在synchronized上第二場大概率也會遇到。我大概有三分之一的知識盲區(qū)是這么補(bǔ)上的。5. 常見問題與避坑指南5.1 不敢投簡歷總覺得“還沒準(zhǔn)備好”這是最大的坑我也經(jīng)歷過。總想著“再刷半個(gè)月題”“再背一遍面經(jīng)”結(jié)果拖了三個(gè)月投出去發(fā)現(xiàn)很多題還是不會。我的建議是給自己設(shè)置一個(gè)“最低可投線”比如“Java集合和MySQL索引能講清楚”就可以開始投。人的狀態(tài)是越面越好準(zhǔn)備永遠(yuǎn)沒有終點(diǎn)。而且真實(shí)面試會告訴你重點(diǎn)在哪比自己埋頭猜有效率得多。5.2 項(xiàng)目被深挖時(shí)手足無措應(yīng)對方法就是提前寫逐字稿。把你的項(xiàng)目故事寫下來每講到一個(gè)技術(shù)點(diǎn)就預(yù)設(shè)追問比如講了Redis就要預(yù)設(shè)“緩存穿透怎么辦”“數(shù)據(jù)不一致怎么辦”“Redis內(nèi)存不夠怎么辦”“淘汰策略了解嗎”。把這些問題寫在項(xiàng)目稿的旁邊反復(fù)練習(xí)直到能條件反射答出來。你會發(fā)現(xiàn)大多數(shù)深挖都跳不出你預(yù)設(shè)的追問圈。5.3 算法題到底要不要刷刷多少如果目標(biāo)是中小公司和多數(shù)二線大廠算法比重沒有想象中那么高但不能完全不準(zhǔn)備。我把算法分成兩條線保底線是LeetCode Hot100里數(shù)組、鏈表、字符串、二叉樹、棧隊(duì)列的簡單和中等題大概40到50題保證“常見題手撕不卡殼”。加分線才是動(dòng)態(tài)規(guī)劃和比較難的題。如果你每天抽一小時(shí)1個(gè)月能刷完保底線。現(xiàn)場面試時(shí)如果卡住了不要死磕可以主動(dòng)和面試官說思路哪怕做不出來也要讓面試官看到你思考和溝通的過程。5.4 面試被問到不會的怎么辦第一反應(yīng)不是慌而是誠實(shí)地說“這塊我了解不多”然后把你已知的相關(guān)內(nèi)容說出來順便請教面試官。大部分面試官不會因?yàn)槟悴粫粋€(gè)偏門知識點(diǎn)就掛掉他們更在意的是你面對未知時(shí)的反應(yīng)。切忌強(qiáng)行編答案面試官一聽就能拆穿結(jié)果比“不會”更差。如果是核心知識點(diǎn)被問到不會比如問MVCC你完全不知道那就說明你前面準(zhǔn)備不到位這次就當(dāng)交學(xué)費(fèi)回去立刻補(bǔ)齊。面試掛了不可怕怕的是每次都掛在不同的地方還不總結(jié)那就真的白面了。5.5 薪資怎么談聊薪資時(shí)不要主動(dòng)報(bào)底價(jià)先問對方公司的薪資結(jié)構(gòu)和區(qū)間。被要求先報(bào)價(jià)時(shí)可以參考招聘JD上的范圍和市場行情報(bào)一個(gè)略高于預(yù)期的數(shù)字。注意薪資談判是正常的商業(yè)行為不用覺得不好意思。但也要結(jié)合自身面試表現(xiàn)合理評估表現(xiàn)好可以適當(dāng)堅(jiān)持表現(xiàn)一般就不要獅子大開口否則容易錯(cuò)過機(jī)會。6. 最后再說點(diǎn)實(shí)話從“兩年CRUD”到社招上岸我最大的感想是面試這件事考的不只是你會多少更是你愿意走出舒適區(qū)多少。CRUD本身不丟人大部分人入行的頭兩年都是這么過來的但如果你準(zhǔn)備跳槽時(shí)還只會講“我做過幾個(gè)簡單的項(xiàng)目”那面試官也只能給你一個(gè)簡單的評價(jià)。我的建議很簡單把準(zhǔn)備面試當(dāng)成一次系統(tǒng)性的知識梳理而不是應(yīng)付考核的背題。借著這個(gè)機(jī)會把Java集合源碼翻一遍把MySQL索引徹底搞懂把Redis的緩存問題想明白這些東西不僅能幫你通過面試也能讓你入職新公司后更有底氣?;仡^來看面試準(zhǔn)備的過程其實(shí)才是這兩年里技術(shù)上成長最快的兩個(gè)月。最后再分享一個(gè)我踩過幾次坑后總結(jié)的經(jīng)驗(yàn)面試不一定非要拿到所有offer才算成功哪怕只拿到一個(gè)哪怕中間掛過幾場只要最后去到的公司比上一家好這個(gè)跳槽就是賺的。希望看到這篇文章的你能少走彎路從容地面完每一場順利上岸。