三、系統設計與問題排查
1、假設你要設計一個 “秒殺系統”,需要考慮高并發、高可用、防超賣等問題,你的整體技術方案是什么?從前端、接口層、服務層、存儲層分別說說核心設計點。
秒殺系統設計
設計核心:瞬時高并發,庫存防超賣,系統穩定性
1)前端層:秒殺未開始時,按鈕置灰,開始后點擊一次后置灰,防止用戶重復點擊。限制單用戶每秒請求次數,避免惡意腳本高頻請求;將秒殺頁面的靜態資源(商品圖片,HTML,CSS)放入CDN,減少源站壓力。
2)接口層:可通過nginx限流,直接攔截惡意請求,或者通過網關限流,Spring Cloud Gateway + Sentinel 實現接口級限流(如秒殺接口 QPS 限制為 5000),超過閾值返回 “請求繁忙”。
3)服務層:首先是消息隊列削峰,秒殺請求不直接調用業務接口,而是發送到MQ中,業務服務從MQ消費請求,將瞬時高并發轉為異步處理;其次是分布式鎖防超賣,用Redis分布式鎖確保同一時間只有一個線程操作庫存,具體應該先扣減Redis的緩存庫存,若緩存庫存<=0,直接返回“已搶完”,再異步同步到數據庫,減少數據庫壓力;然后是庫存預扣與兜底,在秒殺前,將商品庫存預加載到Redis緩存中,避免查詢數據庫,并在數據庫中添加唯一索引,防止因緩存失效導致的重復下單。
4)存儲層:秒殺訂單按用戶ID分表,避免單表寫入壓力過大,并將熱點數據,例如商品庫存,秒殺狀態等放入Redis中,數據庫僅處理最終訂單入庫和庫存同步,在秒殺期間,實現數據庫讀寫分離,訂單查詢走從庫,寫入走主庫,避免主庫壓力過載。
2、項目中如果出現接口響應慢(比如超時),你的排查流程是什么?說明可能的瓶頸點和對應的排查工具。
接口響應慢的排查需遵循從客戶端到存儲層的鏈路順序,結合工具定位瓶頸
1)客戶端層:檢查網絡,可以用ping測試客戶端到服務端的網絡延遲,正常應小于50ms;檢查客戶端配置,查看是否設置了合理的超時時間,是否存在重試邏輯導致請求疊加,舉個例子,用戶反饋app下單慢,排查發現用戶所在地區網絡波動,延遲達500ms,通過cdn調度優化鏈路。
2)網關層:查看網關日志,分析請求在網關的耗時,判斷是否因路由規則復雜,過濾邏輯耗時導致延遲,再檢查網關限流,是否因限流策略過嚴導致請求排隊等待。
3)服務層:查看服務調用鏈路,定位耗時最長的服務;用jstat命令查看GC情況,若YGC頻繁或者Full GC耗時過長,說明JVM內存配置不合理。也可以用jmap 查看存活對象,是否有大對象占用內存,導致GC壓力大。在代碼層面檢查是否有耗時的操作,例如循環調用數據庫,同步RPC調用等,是否使用異步編程優化。
4)存儲層:查看數據庫的慢日志,通過執行計劃分析耗時的Sql,并檢查數據庫連接池,是否因為連接池滿導致請求等待。
3、分布式系統中,如何保證 “分布式事務” 的一致性?你用過哪些方案(?請對比它們的優缺點,并說說你在項目中選擇某方案的原因。
方案 | 實現原理 | 優點 | 缺點 | 實戰場景 |
---|---|---|---|---|
2PC(兩階段提交) | ① 準備階段:協調者通知所有參與者預提交事務;② 提交階段:所有參與者確認后,協調者通知提交 | 一致性強,實現簡單 | 性能差(同步阻塞),協調者單點故障風險高 | 傳統金融核心系統(如銀行轉賬),不敏感于性能 |
TCC(Try-Confirm-Cancel) | ① Try:資源檢查與預留(如凍結庫存);② Confirm:確認執行(如扣減庫存);③ Cancel:回滾(如解凍庫存) | 性能好(異步非阻塞),靈活性高 | 編碼復雜(需手寫 Try/Confirm/Cancel 接口),冪等性難保證 | 電商核心鏈路(訂單創建 + 庫存扣減),高并發場景 |
SAGA 模式 | 將分布式事務拆分為多個本地事務,按順序執行,失敗時按逆序回滾 | 適合長事務(如訂單履約:下單→支付→發貨→確認收貨),無鎖 | 一致性弱(最終一致),回滾邏輯復雜 | 長事務場景(如物流系統、供應鏈系統) |
本地消息表 | ① 業務服務寫本地事務表 + 消息表;② 定時任務掃描消息表,發送消息到 MQ;③ 消費端執行事務并回調確認 | 實現簡單,依賴低(僅需數據庫和 MQ) | 消息表與業務表耦合,需處理消息重復和超時 | 非核心業務(如訂單創建后發送通知、日志同步) |
實戰案例:用 TCC 實現 “訂單創建 + 庫存扣減” 分布式事務
- Try 階段:
- 訂單服務:創建 “待支付” 狀態訂單(本地事務)。
- 庫存服務:檢查庫存是否充足,若充足則凍結庫存(如庫存 100 → 凍結 10,可用 90)。
- Confirm 階段:
- 訂單服務:將訂單狀態改為 “已確認”。
- 庫存服務:將凍結庫存轉為實際扣減(凍結 10 → 扣減 10,庫存 90)。
- Cancel 階段:
- 訂單服務:將訂單狀態改為 “已取消”。
- 庫存服務:解凍凍結的庫存(凍結 10 → 可用 100)。
- 冪等與重試:通過 “事務 ID” 保證 Confirm/Cancel 接口冪等(重復調用不影響結果),用定時任務重試失敗的事務。
4、如果你負責維護一個日活百萬的系統,需要做 “性能優化”,你會從哪些維度入手?比如代碼層面、JVM 層面、架構層面,請結合具體案例說明。
一般來說,性能優化需遵循 “先定位瓶頸,再針對性優化” 的原則,從三個層面入手:
1)代碼層面:通過sql優化或者代碼邏輯優化。sql優化包含查看sql是否走了索引,減少sql中的關聯查詢,使用子查詢代替,避免無效sql;代碼邏輯方面包含減少一些循環嵌套,用對象池復用頻繁創建的對象,如數據庫連接,線程池等,避免頻繁GC。
2)JVM層面:主要是對一些核心參數的調優,例如堆內存配置設置正確,垃圾收集齊選擇正確,元空間和直接內存的參數設置正確等。
3)架構層面:首先是緩存優化,可以使用多級緩存的方式(本地緩存+分布式緩存),熱點數據優先查本地緩存,以及對熱點數據的預熱,在系統啟動時,主動加載熱點數據到緩存中,避免緩存擊穿;然后是異步化,針對非核心流程可采用異步的方式;然后是集群的優化,可以將單服務部署為多實例,通過NGINX或者k8s負載均衡分攤請求,數據庫可實現讀寫分離或者分庫分表。
5、談談你對 “微服務架構” 的理解?微服務拆分的原則是什么?拆分后會帶來哪些問題?你在項目中用什么技術棧解決這些問題?
微服務架構核心理解
微服務架構是將單體應用拆分為多個獨立,可獨立部署,可獨立擴展的小服務,每個服務聚焦于一個業務領域,服務間通過http/gRPC等輕量級協議通信。它的核心優勢在于技術棧靈活,不同服務可選用不同語言/框架,且故障隔離,某服務宕機不影響其他服務,還可以按需擴展。
微服務拆分原則
單一職責原則:一個服務只負責一個業務領域
高內聚低耦合原則:服務間依賴盡可能少,通過接口通信,不直接操作其他服務的數據庫
領域驅動設計原則:按業務領域拆分,如電商系統拆分為用戶域,商品域,支付域,每個域對應一個或多個微服務
避免過度拆分:拆分粒度不宜過細,否則會導致服務間調用復雜,運維成本高
微服務拆分后帶來的問題及解決方案
問題 | 解決技術棧 | 實戰方案 |
---|---|---|
服務注冊與發現 | Spring Cloud Nacos/Eureka | 服務啟動時自動注冊到 Nacos,其他服務通過 Nacos 獲取服務地址(如訂單服務通過 Nacos 找到商品服務地址) |
服務調用復雜 | Spring Cloud OpenFeign/gRPC | 用 OpenFeign 聲明式調用(如?@FeignClient(name = "goods-service") ),簡化 HTTP 調用 |
分布式配置管理 | Spring Cloud Nacos/Apollo | 配置集中存儲在 Nacos,服務動態拉取配置(如不同環境的數據庫地址),無需重啟服務 |
服務限流與熔斷 | Spring Cloud Sentinel/Hystrix | 用 Sentinel 為訂單服務設置 QPS 限流(如 5000),服務異常時熔斷(返回默認數據),避免級聯故障 |
分布式事務 | Seata/TCC/SAGA | 用 Seata 框架實現 AT 模式(自動補償),簡化分布式事務開發(如訂單服務與庫存服務的事務) |
鏈路追蹤 | SkyWalking/Pinpoint | 部署 SkyWalking 代理,追蹤服務調用鏈路,定位跨服務調用的耗時瓶頸(如訂單服務→支付服務耗時 1.5s) |
四、綜合能力與工程實踐(考察經驗與職業素養)
1、過去五年的開發經歷中,你認為自己做過的最有挑戰性的項目是什么?請說說項目背景、你的角色、遇到的核心問題,以及你是如何解決的。
開放性問題,針對自己項目作答
2、在團隊協作中,你是如何保證代碼質量的?比如代碼評審的流程、單元測試的覆蓋率要求、使用過哪些靜態代碼分析工具?如果發現同事的代碼有潛在問題,你會如何溝通解決?
CR流程:
前置檢查:開發者提交代碼前,先自查(是否符合編碼規范、是否有注釋、是否通過單元測試)。
提交規范:用 Git 提交模板,要求提交信息包含 “類型(feat/fix)+ 模塊 + 描述”(如?feat(order): 新增訂單取消接口
)
CR 執行:多人評審:至少 1 名資深工程師 + 1 名同模塊開發者評審,重點檢查邏輯正確性、性能問題、潛在 Bug。工具輔助:用 GitLab CI 自動觸發 CR 流程,未通過評審的代碼無法合并到主分支。
單元測試與覆蓋率
單元測試框架:用 JUnit 5 + Mockito 編寫單元測試,Mock 外部依賴(如用 Mockito 模擬 Redis 客戶端,避免依賴真實 Redis)。
覆蓋率要求:核心業務代碼(如訂單創建、支付邏輯)覆蓋率 ≥ 80%,工具用 JaCoCo 生成覆蓋率報告,SonarQube 監控覆蓋率達標情況。
3、你平時是如何保持技術學習的?最近半年關注過哪些 Java 生態的新技術?有沒有嘗試在項目中落地過新技術,或者通過技術博客、開源貢獻等方式輸出過經驗?
開放性問題,針對個人回答
4、假設你負責的服務需要從 “單體架構” 遷移到 “微服務架構”,你會如何制定遷移計劃?如何平衡 “業務迭代” 和 “架構遷移” 的優先級?遷移過程中如何保證服務的平穩過渡?
遷移原則
循序漸進:不追求 “一刀切”,分階段拆分,優先拆分低風險、高收益的模塊(如先拆分用戶服務,再拆分訂單服務)。
業務優先:遷移期間保證業務正常迭代,新功能優先在微服務中開發,舊功能逐步遷移。
灰度發布:每個服務遷移后,通過灰度發布(如 10% 流量切到新服務)驗證穩定性,無問題后全量切換。
遷移步驟(總6個月)
1)準備階段(1個月):搭建微服務基礎設施:部署 Nacos(服務注冊與配置)、SkyWalking(鏈路追蹤)、Sentinel(限流熔斷);定義通用規范:接口規范(RESTful)、代碼規范、數據庫分庫分表規范、日志規范。
2)核心服務拆分(3個月):拆分用戶服務(用戶注冊、登錄、信息查詢),獨立部署,通過 API 網關對外提供服務,舊單體系統調用新服務。拆分商品服務(商品管理、庫存查詢),與用戶服務類似,逐步切換流量。拆分訂單服務(核心,難度最高),需解決分布式事務(用 Seata)、庫存扣減等問題,先在非峰值時段灰度切換。
3)非核心服務拆分(1個月):拆分日志服務、通知服務(短信、郵件),這些服務依賴少,遷移風險低。
4)收尾階段:逐步下線單體系統功能,僅保留少量未遷移的舊功能,調整服務集群規模、優化緩存策略、完善監控告警。
平衡業務迭代
優先級排序:新業務需求若與遷移模塊相關(如訂單服務新功能),優先在微服務中開發;與遷移無關的需求(如后臺管理功能),在單體系統中開發,后續再遷移
回滾機制:每個服務遷移后,保留單體系統的舊接口,若新服務出現問題,可通過網關快速切回舊接口(藍綠部署),保證業務不中斷
5、作為一名有五年經驗的工程師,你未來 1-2 年的職業規劃是什么?是傾向于 “技術專家”,還是 “技術管理”?為什么?
根據個人實際情況回答,下面舉個例子
職業定位:技術專家
未來1-2年,傾向于深耕分布式系統架構和性能優化,成為團隊的技術專家,而非純管理崗。
選擇原因:
興趣驅動:相比團隊管理,我更享受通過技術解決復雜問題的過程(如秒殺系統高并發優化、分布式事務一致性保障),對底層原理和架構設計有強烈的探索欲。
價值體現:在高并發、高可用場景下,技術專家能直接解決業務痛點(如將系統 QPS 從 1000 提升到 10000),為業務增長提供技術支撐,這種價值感更直接。
團隊需求:當前團隊微服務架構剛落地,在性能優化、分布式問題排查等方面缺乏資深專家,我希望通過深耕該領域,填補團隊技術短板。
1-2年目標與行動計劃
技術深耕:深入研究云原生技術(Kubernetes、Istio、Serverless),考取 CKA(Certified Kubernetes Administrator)認證。主導 1-2 個核心項目的架構優化(如將現有微服務遷移到 Kubernetes,實現彈性伸縮)。
團隊賦能:編寫《分布式系統問題排查手冊》,沉淀團隊經驗,降低新人學習成本;每月組織 1 次技術分享,主題涵蓋性能優化、架構設計等,提升團隊整體技術水平。
外部輸出:在行業會議(如 ArchSummit)分享微服務實踐經驗,提升個人行業影響力。參與開源項目(如 Spring Cloud Alibaba),貢獻代碼或文檔,拓展技術視野。