方案:從單機(jī)Session到Redis高可用的實(shí)踐)
先說結(jié)論這場面試?yán)锩嬖嚬賳柕摹胺植际綍挼囊恢滦院腿轂?zāi)方案”并不是讓你背一兩個Redis命令就完事它考察的是你從單機(jī)Session到分布式Session演進(jìn)過程中的完整思考鏈路。Java崗位但凡涉及到電商、物流、金融這類線上業(yè)務(wù)分布式會話都是繞不開的話題因?yàn)橹灰?wù)一上集群用戶登錄狀態(tài)怎么存、節(jié)點(diǎn)掛了怎么辦、會話丟了能不能忍這些問題立刻變成線上事故級別的風(fēng)險(xiǎn)。這篇文章我就把這套問題的完整回答邏輯拆給你包括方案選型背后的取舍、一致性細(xì)節(jié)的坑、容災(zāi)架構(gòu)怎么搭以及面試現(xiàn)場的高頻追問怎么接。準(zhǔn)備面試的同學(xué)可以直接拿來當(dāng)復(fù)習(xí)提綱正在做分布式改造的工程師也可以對照檢查自己的會話方案有沒有漏洞。1. 面試為什么要問這道題——先搞懂面試官的意圖1.1 這道題考的其實(shí)不是“會不會配Redis”很多候選人一聽到“分布式會話”張口就是“用Redis存Session”然后開始背Spring Session配置。說實(shí)話這種回答在面試官眼里只能拿及格分因?yàn)樗鼪]有展示出任何設(shè)計(jì)決策能力。我面過不少人能完整把“為什么不用Session復(fù)制、為什么不用粘性會話、最終方案的一致性邊界在哪、Redis掛了怎么辦”講透的占比不到三成。這道題真正考察的是三個層面的東西。第一層是基礎(chǔ)知識懂不懂HttpSession原本是單機(jī)內(nèi)存里的東西服務(wù)端是怎么靠Cookie里的JSessionId來回識別用戶的。第二層是架構(gòu)思維知不知道集群化以后會話狀態(tài)面臨什么新問題能不能從“無狀態(tài)化”的角度重新設(shè)計(jì)。第三層是工程落地知不知道Redis方案里序列化、過期續(xù)期、主從切換丟數(shù)據(jù)這些真實(shí)存在的細(xì)節(jié)。大多數(shù)候選人卡在第二層和第三層之間能說出“用Redis”但說不清“用了Redis之后還有什么坑”。1.2 分布式會話問題是被業(yè)務(wù)硬逼出來的要理解這個問題的本質(zhì)得先回顧一下Session的歷史。最早的單機(jī)Web應(yīng)用里HttpSession就是Tomcat進(jìn)程內(nèi)存中的一塊數(shù)據(jù)用戶登錄后服務(wù)端把Session對象存在本地同時通過Cookie把一個唯一標(biāo)識發(fā)給瀏覽器下次請求帶著這個標(biāo)識Tomcat再從內(nèi)存里找對應(yīng)的Session。這套機(jī)制在單機(jī)時代非常流暢查Session就是一次內(nèi)存Map查找性能極高。問題出在服務(wù)集群化之后。一臺Tomcat扛不住流量要橫向擴(kuò)容成N臺Nginx在前面做負(fù)載均衡。這時候一個用戶第一次請求落在節(jié)點(diǎn)A登錄狀態(tài)存在了A的內(nèi)存里第二次請求被負(fù)載均衡轉(zhuǎn)發(fā)到了節(jié)點(diǎn)BB的內(nèi)存里根本沒有這個Session用戶就變成了未登錄狀態(tài)。這就是分布式會話問題最原始的樣子。解決辦法不外乎三條路讓Session在節(jié)點(diǎn)間同步、讓同一個用戶始終訪問同一臺節(jié)點(diǎn)、或者把Session從節(jié)點(diǎn)內(nèi)存中拿出來放到一個所有節(jié)點(diǎn)共享的地方。面試官問這道題本質(zhì)上就是看你有沒有經(jīng)歷過這個從“單機(jī)內(nèi)存存儲”到“共享外部存儲”的架構(gòu)演進(jìn)過程。理解了背景你才能說出來為什么集中式存儲是主流而不是機(jī)械地背方案。2. 三種經(jīng)典方案對比從Session復(fù)制到集中存儲2.1 方案一Session復(fù)制——簡單但天花板低Session復(fù)制是最早的集群會話解決方案典型實(shí)現(xiàn)是Tomcat自帶的Cluster機(jī)制通過DeltaManager在集群節(jié)點(diǎn)之間廣播Session變更。一個節(jié)點(diǎn)上創(chuàng)建或修改了Session序列化之后廣播給其他所有節(jié)點(diǎn)保證每臺Tomcat的內(nèi)存里都有一份完整的Session數(shù)據(jù)。這樣任何一臺節(jié)點(diǎn)掛了用戶的請求轉(zhuǎn)發(fā)到別的節(jié)點(diǎn)依然能找回會話狀態(tài)。這個方案有一個致命問題是廣播風(fēng)暴。集群里節(jié)點(diǎn)數(shù)量一多Session數(shù)據(jù)的復(fù)制量按幾何級數(shù)增長每次請求更新Session都意味著全網(wǎng)廣播非常消耗帶寬和CPU。而且每臺節(jié)點(diǎn)都冗余存儲所有Session內(nèi)存利用率低。我見過一個小型集群三臺Tomcat跑Session復(fù)制高峰期光復(fù)制流量就占了三成帶寬后來不得不換方案。所以Session復(fù)制適合集群規(guī)模極小、會話更新頻率低的系統(tǒng)大規(guī)模生產(chǎn)環(huán)境基本頂不住。2.2 方案二粘性會話——最省事但隱患很大粘性會話的做法是在負(fù)載均衡層做文章通過Nginx的ip_hash或者按請求參數(shù)取哈希把同一個用戶的請求始終轉(zhuǎn)發(fā)到同一臺后端節(jié)點(diǎn)。這樣一來Session自然就留在固定的節(jié)點(diǎn)上不需要任何中間件也不改代碼。這個方案的代價是把可用性寄托在一臺節(jié)點(diǎn)上。一旦這臺節(jié)點(diǎn)宕機(jī)或者重啟它上面保存的所有用戶會話瞬間丟失而且因?yàn)楣R?guī)則通常依賴來源IP這本身就有問題。移動網(wǎng)絡(luò)下用戶的出口IP是會變的切換一次基站IP變了哈希就對應(yīng)到了另一臺節(jié)點(diǎn)會話照樣找不到。另外還有負(fù)載不均的煩惱某個IP段的用戶量大對應(yīng)節(jié)點(diǎn)就忙死其他節(jié)點(diǎn)空轉(zhuǎn)。粘性會話作為短期過渡方案可以但凡是追求高可用的業(yè)務(wù)都不能把它當(dāng)長期方案。2.3 方案三集中式會話存儲——目前的主流選擇集中式存儲的思路是把Session從業(yè)務(wù)節(jié)點(diǎn)剝離出來放進(jìn)獨(dú)立的共享存儲中間件最典型的就是Redis也有用MySQL或者內(nèi)存數(shù)據(jù)庫的。Spring Session框架就是干這個事的它把原本存在Tomcat內(nèi)存里的Session數(shù)據(jù)改成序列化后寫入Redis同時把會話標(biāo)識繼續(xù)保持在Cookie里。任何一個業(yè)務(wù)節(jié)點(diǎn)收到請求都能直接從Redis讀取會話數(shù)據(jù)業(yè)務(wù)節(jié)點(diǎn)徹底變?yōu)闊o狀態(tài)想怎么擴(kuò)容就怎么擴(kuò)容。用Redis存Session為什么能成為主流有一個很底層的優(yōu)勢Redis是單線程模型執(zhí)行GET、SET、HSET這些命令天然具備原子性在多節(jié)點(diǎn)并發(fā)讀寫同一個會話數(shù)據(jù)時不容易出現(xiàn)競態(tài)條件。配合數(shù)據(jù)過期機(jī)制正好可以實(shí)現(xiàn)會話超時失效。集中式方案也不是沒缺點(diǎn)最明顯的是每次請求多了一次網(wǎng)絡(luò)I/O原來本地內(nèi)存取數(shù)據(jù)微秒級現(xiàn)在走一次網(wǎng)絡(luò)至少幾毫秒這意味著業(yè)務(wù)接口的延遲會增加。同時對Redis本身的穩(wěn)定性要求變得極高Redis一掛所有節(jié)點(diǎn)的會話全部失效這就是后面容災(zāi)方案要解決的核心問題。三種方案放在一起看各自的定位就很清晰了方案一致性可用性擴(kuò)展性適合場景Session復(fù)制強(qiáng)一致但代價高節(jié)點(diǎn)級冗余差節(jié)點(diǎn)越多越慢小規(guī)模集群粘性會話天然一致節(jié)點(diǎn)宕機(jī)即丟擴(kuò)容不均短期過渡集中式存儲取決于后端存儲依賴Redis高可用極好無狀態(tài)中大型線上業(yè)務(wù)3. 會話一致性的核心難點(diǎn)與落地細(xì)節(jié)3.1 先搞清楚“一致性”在這里究竟指什么“一致性”這個詞在分布式系統(tǒng)里出現(xiàn)過太多次容易跟分布式事務(wù)的ACID混在一起。面試官問“分布式會話的一致性”核心關(guān)注點(diǎn)是同一個用戶在任意一臺業(yè)務(wù)節(jié)點(diǎn)上讀到自己的Session狀態(tài)都應(yīng)該是相同且最新的。用戶在前一個請求里更新了購物車后一個請求不論被負(fù)載均衡轉(zhuǎn)發(fā)到哪臺節(jié)點(diǎn)看到的購物車內(nèi)容都必須包含前一次更新。這里的一致性更多是“讀寫一致性”和“并發(fā)更新一致性”。讀寫一致性指的是用戶剛寫入的數(shù)據(jù)在這次會話隨后的讀取中一定能看到并發(fā)更新一致性指的是同一會話在并發(fā)請求場景下后寫入的數(shù)據(jù)不能丟失比如說兩個請求同時修改用戶信息一個改昵稱一個改頭像最終Session里兩個字段都應(yīng)該被更新。在Redis集中存儲方案下讀寫一致性天然滿足因?yàn)樗泄?jié)點(diǎn)共享同一個存儲難點(diǎn)主要在并發(fā)更新和邊界情況。3.2 Redis會話方案里容易被忽略的四個細(xì)節(jié)第一個細(xì)節(jié)是并發(fā)寫覆蓋問題。如果Session在Redis里存成一個整體字符串比如直接SET一個序列化之后的JSON對象。兩個并發(fā)請求同時讀到原始數(shù)據(jù)各自修改自己的JSON字段再整體寫回先完成的會被后完成的覆蓋改昵稱的請求如果晚到半秒頭像修改就丟了。解決做法是盡量用Hash結(jié)構(gòu)存Session屬性不同字段用獨(dú)立的HSET命令更新或者引入版本號做樂觀鎖更新時攜帶版本號Redis里的版本號匹配才允許寫入。第二個細(xì)節(jié)是過期續(xù)期。Session有超時時間常見的是30分鐘無操作則失效。用Redis的TTL實(shí)現(xiàn)時要注意用戶每次訪問Session都必須刷新過期時間嚴(yán)格來說是滑動過期。實(shí)現(xiàn)上可以通過每次操作都EXPIRE一次但要注意大量Session在同一秒集中過期會導(dǎo)致Redis的過期掃描造成短暫的阻塞把過期時間設(shè)置加上隨機(jī)偏移會更穩(wěn)妥。另外框架內(nèi)置的過期刷新通常攔截請求后自動完成如果自己手寫方案要特別注意別漏了這一步否則用戶用著用著突然被踢下線。第三個細(xì)節(jié)是序列化方式。Java對象放進(jìn)Redis必須序列化JDK原生序列化最省事但有兩個問題一是序列化后的二進(jìn)制體積大二是存在反序列化安全風(fēng)險(xiǎn)。線上生產(chǎn)環(huán)境我建議用JSON或者二進(jìn)制序列化方案同時要注意存儲字段類型變化時對舊數(shù)據(jù)的兼容處理。曾遇到過線上升級字段類型后反序列化報(bào)TypeMismatch異常所有在線用戶會話讀取失敗這屬于演進(jìn)過程中必須考慮的兼容性設(shè)計(jì)。第四個細(xì)節(jié)是時間基準(zhǔn)。會話過期時間、刷新時間的計(jì)算在分布式環(huán)境下不能依賴各業(yè)務(wù)節(jié)點(diǎn)的本地時鐘而應(yīng)該統(tǒng)一交給Redis用TTL來管理。業(yè)務(wù)節(jié)點(diǎn)只負(fù)責(zé)發(fā)命令不參與時間計(jì)算這樣就避免了不同節(jié)點(diǎn)時鐘偏差導(dǎo)致的過期時間不一致。3.3 會話一致性和分布式事務(wù)不是一回事這里插一句很多準(zhǔn)備面試的同學(xué)容易把“會話一致性”跟“分布式事務(wù)”混著答。面試官如果問的是分布式會話的一致性指的是上面說的會話狀態(tài)讀寫一致問題屬于可容忍短時不確定性的“最終一致”范疇。而分布式事務(wù)一致性討論的是多個服務(wù)間數(shù)據(jù)變更的原子性比如下單扣庫存這種跨服務(wù)事務(wù)需要用TCC、消息事務(wù)這些機(jī)制來保證。兩者維度不同混在一起回答會讓面試官覺得概念體系混亂。如果你主動區(qū)分開說“會話一致性更關(guān)注狀態(tài)可找回分布式事務(wù)關(guān)注的是多服務(wù)數(shù)據(jù)原子性”這反而是加分項(xiàng)。4. 容災(zāi)方案設(shè)計(jì)從Redis高可用到端上兜底4.1 Redis服務(wù)端容災(zāi)持久化、主從、哨兵、Cluster集中式存儲把所有會話雞蛋放進(jìn)Redis一個籃子里容災(zāi)方案就得圍繞Redis本身來設(shè)計(jì)。第一層是持久化Redis默認(rèn)的RDB快照是周期性保存全量數(shù)據(jù)主進(jìn)程fork一個子進(jìn)程做快照性能影響小但最后一次快照之后的數(shù)據(jù)在宕機(jī)時會丟。AOF日志則是記錄每條寫命令重啟時重放恢復(fù)數(shù)據(jù)丟失窗口更小。生產(chǎn)環(huán)境通常兩者結(jié)合同時開啟RDB做整點(diǎn)快照AOF做實(shí)時日志具體丟失量還得看AOF刷盤策略最安全的always模式每條命令都刷盤但性能代價很大。第二層是高可用架構(gòu)。最經(jīng)典的是主從加哨兵模式一個Redis主節(jié)點(diǎn)負(fù)責(zé)讀寫一個或多個從節(jié)點(diǎn)同步數(shù)據(jù)哨兵進(jìn)程負(fù)責(zé)監(jiān)控。主節(jié)點(diǎn)掛掉時哨兵集群進(jìn)行故障轉(zhuǎn)移自動把一個從節(jié)點(diǎn)提升為新主節(jié)點(diǎn)業(yè)務(wù)端通過哨兵感知新的主節(jié)點(diǎn)地址。這套方案能解決單點(diǎn)故障但要注意主從復(fù)制是異步的主節(jié)點(diǎn)宕機(jī)瞬間尚未同步到從節(jié)點(diǎn)的數(shù)據(jù)會丟如果這期間正好有用戶更新了Session這部分更新就會丟失。第三層是Redis Cluster模式適合數(shù)據(jù)量超大、單機(jī)內(nèi)存不夠的場景。Cluster將數(shù)據(jù)按哈希槽分布在多個主節(jié)點(diǎn)上每個主節(jié)點(diǎn)配一個或多個從節(jié)點(diǎn)主節(jié)點(diǎn)掛了自動從從節(jié)點(diǎn)晉升。Cluster的好處是容量可以橫向擴(kuò)展壞處是架構(gòu)復(fù)雜度高槽位遷移、多key操作限制、請求重定向都是需要處理的問題。對大多數(shù)會話業(yè)務(wù)來說單機(jī)內(nèi)存通常夠用先上主從配合哨兵是性價比最高的選擇只有數(shù)據(jù)量真的突破單機(jī)內(nèi)存再考慮Cluster。4.2 客戶端側(cè)的降級與兜底策略Redis高可用只能降低故障概率不等于避免數(shù)據(jù)丟失。會話數(shù)據(jù)的丟失最壞結(jié)果就是用戶被強(qiáng)制下線需要重新登錄。這聽起來好像是災(zāi)難但在真正的系統(tǒng)設(shè)計(jì)里這是可接受的兜底結(jié)果反而應(yīng)該把精力放在怎么讓用戶“無感”或“低感”地恢復(fù)正常。一個關(guān)鍵的策略是會話降級與靜默登錄。如果業(yè)務(wù)系統(tǒng)有自己的Token體系比如JWT或者OAuth2的AccessToken在Redis不可用的窗口期可以放棄從Redis讀取Session而是根據(jù)Token里的簽名信息恢復(fù)一個最小化的登錄態(tài)讓用戶繼續(xù)訪問不需要登錄態(tài)的接口。核心交易類操作可以提示“請重新登錄”而瀏覽類接口保持可用。這樣把Redis故障的影響面從“全站不可用”縮小成“部分敏感操作降級”。另一個策略是網(wǎng)關(guān)層和本地緩存兜底。在Spring Session方案里業(yè)務(wù)節(jié)點(diǎn)本地可以加一個短時間的一級緩存比如Guava Cache或者Caffeine緩存最近幾十秒內(nèi)的Session讀取結(jié)果避免每個請求都穿透到Redis。Redis故障時本地緩存雖然只能覆蓋一小段時間的會話數(shù)據(jù)但至少能讓部分高頻用戶維持短期訪問為故障恢復(fù)爭取時間。還有一點(diǎn)必須提的是防雪崩。Redis掛了以后如果所有請求都嘗試去Redis讀寫會話會不斷連接超時大量線程阻塞在I/O上拖垮整個應(yīng)用。需要在調(diào)用Redis時設(shè)置嚴(yán)格的超時時間同時配合線程池隔離讓Redis故障只影響會話相關(guān)線程不影響其他業(yè)務(wù)。我曾經(jīng)處理過Redis宕機(jī)導(dǎo)致業(yè)務(wù)線程池被打滿的線上事故當(dāng)時就是沒設(shè)置合理的讀取超時教訓(xùn)很深刻。4.3 一套可落地的容災(zāi)組合與演練建議把上面這些串起來一套可落地的組合方案就清晰了。Redis層面采用主從加哨兵架構(gòu)開啟AOF持久化AOF刷盤策略根據(jù)業(yè)務(wù)對數(shù)據(jù)丟失的容忍度選擇一秒鐘刷一次或每次寫入都刷盤。業(yè)務(wù)層面使用Spring Session管理統(tǒng)一設(shè)置會話超時時間并處理滑動過期。外部接口層設(shè)置Redis讀寫超時通常網(wǎng)絡(luò)超時50-100ms連接超時200-500ms配合本地短時緩存做一級兜底。最外層的登錄體系做好靜默續(xù)期保證Redis故障時用戶不登錄也能繼續(xù)瀏覽寫操作時給出明確提示。這套方案上線后建議做故障演練。最簡單的做法是直接kill掉Redis主節(jié)點(diǎn)進(jìn)程觀察哨兵能否在預(yù)期時間內(nèi)完成主從切換期間請求的失敗率是多少有多少用戶被踢下線本地緩存命中率如何。更進(jìn)一步的演練可以模擬網(wǎng)絡(luò)分區(qū)、Redis進(jìn)程假死、哨兵節(jié)點(diǎn)超半數(shù)不可用等場景。混沌工程領(lǐng)域有一句話沒有演練過的容災(zāi)方案不叫方案叫PPT。演練的價值在于提前暴露設(shè)計(jì)缺口而不是等線上事故來檢驗(yàn)。5. 面試應(yīng)答策略與高頻追問應(yīng)對5.1 我建議的回答框架先統(tǒng)籌、再分層、后細(xì)節(jié)現(xiàn)場回答時不必從Session歷史開始啰嗦我建議按“方案選型—數(shù)據(jù)一致性—容災(zāi)降級”三段式組織每段控制在30秒到1分鐘。第一段直接說結(jié)論“我會把Session從業(yè)務(wù)節(jié)點(diǎn)內(nèi)存中剝離用Redis做集中式存儲通過Spring Session接入使業(yè)務(wù)節(jié)點(diǎn)無狀態(tài)化?!比缓笱a(bǔ)充一句“為什么不選Session復(fù)制和粘性會話”說明廣播風(fēng)暴和節(jié)點(diǎn)宕機(jī)丟會話的缺點(diǎn)展現(xiàn)你不是背答案而是做過比較。第二段講一致性說清楚Redis集中存儲天然保證了讀取一致性并發(fā)更新場景用Hash結(jié)構(gòu)和字段級寫入規(guī)避覆蓋風(fēng)險(xiǎn)同時注意序列化方式、過期續(xù)期、統(tǒng)一時間基準(zhǔn)這幾個細(xì)節(jié)。第三段講容災(zāi)從Redis持久化、主從哨兵架構(gòu)講起講到客戶端側(cè)超時控制、本地兜底緩存、登錄靜默續(xù)期和降級策略最后提一句“關(guān)鍵環(huán)節(jié)經(jīng)過演練驗(yàn)證過得失”。這樣一套下來面試官能感覺到你既有宏觀架構(gòu)能力又踩過工程落地階段的坑。5.2 高頻追問與應(yīng)對思路追問一“Redis掛了你的Session全部丟失能接受嗎”這個問題要區(qū)分接受邊界作為設(shè)計(jì)者要承認(rèn)閾值客觀存在但會議論里強(qiáng)調(diào)通過主從切換將丟失窗口縮小到秒級以下通過登錄靜默續(xù)期讓用戶感知降至最低核心交易場景強(qiáng)制重新登錄保安全。面試官考察的是你有沒有真實(shí)感知到“任何方案都不是銀彈”而不是讓你說一個絕對不會丟的方案。追問二“為什么不直接用粘性會話省掉Redis不是更簡單”可以回答粘性會話的問題在于節(jié)點(diǎn)宕機(jī)導(dǎo)致會話永久丟失且負(fù)載不均無法水平擴(kuò)展。而集中式存儲犧牲一次網(wǎng)絡(luò)I/O換來的是節(jié)點(diǎn)故障不再影響會話業(yè)務(wù)節(jié)點(diǎn)可以隨時擴(kuò)縮容從長期看收益遠(yuǎn)超成本。追問三“Session的過期時間怎么設(shè)計(jì)失效后怎么處理”可以從業(yè)務(wù)角度回答普通用戶30分鐘較敏感的操作場景適當(dāng)縮短管理后臺可以更短。過期后提供兩種處理主動清除Redis中的會話數(shù)據(jù)釋放內(nèi)存同時前端通過返回的會話失效狀態(tài)引導(dǎo)用戶重新登錄。還可以補(bǔ)充一句“滑動過期需要每次操作刷新TTL注意避免集中過期導(dǎo)致Redis阻塞”。追問四“Redis主從切換期間用戶請求打過來怎么辦”這個追問逼的就是你是否理解異步復(fù)制丟失數(shù)據(jù)和切換耗時的影響??梢韵冉忉屔诒鴻C(jī)制完成主從切換需要十幾秒這期間寫操作會失敗然后說明業(yè)務(wù)側(cè)的應(yīng)對Redis客戶端庫通常在連接失敗后自動重連并跟隨新主節(jié)點(diǎn)配合超時降級策略讓大部分請求快速失敗而不是長時間阻塞。追問五“如果讓你們公司現(xiàn)有系統(tǒng)接入這套方案你第一步做什么”這個問題考察落地能力先說評估現(xiàn)狀當(dāng)前是單機(jī)部署還是集群、是否有Session復(fù)制、用戶量級和會話超時策略是什么。再說明落地順序先把Redis高可用架構(gòu)搭好再接入Spring Session替換原有Session Manager灰度發(fā)布時兩種方案并行觀察線上會話命中率和日志確認(rèn)穩(wěn)定后再完全切換。這個回答能極大加分因?yàn)槊嬖嚬俾牭贸瞿阌姓鎸?shí)的改造經(jīng)驗(yàn)。聊到這兒這套方案本身就比較完整了。我個人在實(shí)際面試中稍微總結(jié)了一下當(dāng)對方問到分布式會話時最忌諱的是直接跳到Redis命令級細(xì)節(jié)最加分的是先把方案選型的“為什么”講清楚再落到真實(shí)世界里“哪里會丟數(shù)據(jù)、怎么兜底”的層面。做Java開發(fā)這些年凡是線上會話出過事故的同學(xué)回過頭再回答這類問題基本都能答得很扎實(shí)原因無他被故障教育過的設(shè)計(jì)才經(jīng)得住追問。如果你近期也在準(zhǔn)備面試建議拿這套框架先練一遍手遇到不懂的細(xì)節(jié)就回到線上環(huán)境里親手壓一壓效果比背十遍八股文都強(qiáng)。