據(jù)筆試復(fù)盤:題型解析與備考策略)
看到“觸寶科技2017秋季校招筆試后端大數(shù)據(jù)第三批”這個標(biāo)題估計會有人覺得奇怪2017年觸寶的校招筆試現(xiàn)在翻出來講還有什么意義我當(dāng)時的感受可以說明白因為那一批筆試的題目結(jié)構(gòu)幾乎就是市面上“后端大數(shù)據(jù)”崗位筆試的標(biāo)準(zhǔn)樣本。如果你現(xiàn)在正在準(zhǔn)備這個方向的校招把這份卷子的考察邏輯吃透再去看別家公司的筆試題思路會清晰很多很多考點其實是換湯不換藥。我是怎么拿到這份題的這得從當(dāng)年的秋招時間線說起。2017年9月底我投遞簡歷的動作比同寢室同學(xué)慢了一拍大部分公司的提前批已經(jīng)結(jié)束。觸寶這家的筆試通知來得也比較晚郵件標(biāo)題就是“觸寶科技2017秋季校招筆試后端大數(shù)據(jù)第三批”??吹健暗谌比齻€字我還愣了一下——原來筆試還要排隊。實際秋招高峰期互聯(lián)網(wǎng)公司筆試名額必須分批消化一是簡歷量太大在線筆試平臺一下子扛不住那么多并發(fā)二來也方便面試官分批發(fā)面試邀請。觸寶那年秋招大致安排了四五批筆試第三批大約在10月中下旬在牛客網(wǎng)在線答題時長兩小時客觀題、簡答題和編程題混合在一張卷子里。我考完當(dāng)天趁著記憶還熱乎把題目和答案整理了一遍這些內(nèi)容就是下面復(fù)盤的基礎(chǔ)。1. 還原“第三批”試卷題型結(jié)構(gòu)與崗位畫像1.1 為什么校招筆試要分批次第三批意味著什么先聊一個常被忽略的問題為什么校招筆試要分批次。很多同學(xué)誤以為分批次是“補錄”或者“沒招滿”其實不完全是。觸寶這種體量的公司在秋招季面對的簡歷量是幾萬份筆試平臺同一時間承載幾千人同時在線做題雖然技術(shù)上可行但后續(xù)的判卷、簡歷篩選、面試邀約節(jié)奏都會被拖垮。分批次的真正目的是讓招聘節(jié)奏可控第一批考完面試官同步篩簡歷和卷子邊面邊等后面幾批的卷子等第三批考完前面通過的人可能已經(jīng)進了終面這時企業(yè)手上會有一個完整的候選人池綜合對比后再發(fā)offer。第三批意味著什么時間上已經(jīng)過了國慶部分同學(xué)的秋招心態(tài)開始浮躁周圍有人已經(jīng)拿到意向書正在準(zhǔn)備筆試的人多少會焦慮。但企業(yè)視角下第三批的卷子和第一批的難度并不會差很多考察重點一般也不會變——筆試考的是通用能力不會因為批次靠后就故意放水。我印象里觸寶的筆試邀請郵件里還附了一句“如時間沖突可申請調(diào)整批次日程”說明批次安排主要是為了保證考試秩序不存在第三批更簡單這種事。1.2 試卷整體結(jié)構(gòu)與崗位技術(shù)棧信號先說題型分布。我用一張表還原當(dāng)時卷面的結(jié)構(gòu)具體分值順序可能有偏差但題型構(gòu)成和核心題源我記憶比較深題型大致題量考察方向單選多選約20題Java基礎(chǔ)、并發(fā)、網(wǎng)絡(luò)、操作系統(tǒng)、大數(shù)據(jù)基礎(chǔ)簡答與問答4-5題HDFS原理、MapReduce、數(shù)據(jù)傾斜、Spark與MR對比在線編程3題數(shù)組、字符串、哈希相關(guān)的算法題場景設(shè)計1題從埋點到報表的離線數(shù)據(jù)鏈路設(shè)計從這個結(jié)構(gòu)能明顯看出這個崗位的畫像它不是純粹的后端開發(fā)也不是純粹的數(shù)據(jù)平臺開發(fā)而是“后端基礎(chǔ)大數(shù)據(jù)處理”的混合體。觸寶的產(chǎn)品線當(dāng)年以輸入法、通訊工具類應(yīng)用為主海外用戶占比高日活數(shù)據(jù)量可觀用戶行為日志、詞庫糾錯日志、推送轉(zhuǎn)化日志都需要穩(wěn)定的大數(shù)據(jù)鏈路去處理。因此后端大數(shù)據(jù)崗要的人既要有Java服務(wù)的功底也要理解Hadoop生態(tài)的離線計算方式。整個卷面透露出的技術(shù)棧信號很清晰語言以Java為主觸寶后端當(dāng)時大量使用Java大數(shù)據(jù)側(cè)圍繞HDFS、Hive、Spark這套體系SQL能力是隱含考察點算法題不考特別偏的動態(tài)規(guī)劃側(cè)重基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)和邊界處理。我當(dāng)時翻完卷子心里就有了數(shù)這家的筆試比較務(wù)實不是“我考倒你”的姿態(tài)而是“我把日常工作濃縮成題目看看你合不合適”。2. 基礎(chǔ)題復(fù)盤Java并發(fā)與網(wǎng)絡(luò)知識里的“送分題”和“陷阱題”2.1 HashMap原理為什么年年考考的核心是什么第一大題里幾乎必有HashMap這次也不例外。當(dāng)時的題目大致是“簡述HashMap的put流程JDK8中引入紅黑樹解決了什么問題HashMap為什么線程不安全”這類題放在今天的筆試?yán)镆廊皇浅?秃诵目疾禳c分三層第一層是能不能說清put的完整流程。先計算key的hash值經(jīng)過擾動函數(shù)高16位與低16位異或降低碰撞概率然后定位到數(shù)組下標(biāo)如果該位置為空就直接放入不為空就遍歷鏈表存在相同key則覆蓋否則尾插。JDK8里鏈表長度超過閾值8且數(shù)組長度達(dá)到64時鏈表轉(zhuǎn)紅黑樹目的是把最壞情況下的查找時間從O(n)降到O(logn)。第二層是要理解為什么線程不安全。我當(dāng)時的回答分了三個角度并發(fā)put可能導(dǎo)致數(shù)據(jù)覆蓋因為多個線程同時判斷相同位置為空時都會執(zhí)行插入后寫覆蓋先寫擴容時在高并發(fā)場景下JDK7可能出現(xiàn)鏈表環(huán)雖然JDK8改了插入方式不再有環(huán)但數(shù)據(jù)丟失和覆蓋問題依然存在size字段也不是線程安全的。第三層是引申問法HashMap和ConcurrentHashMap的區(qū)別。這個話題后面面試也被追問過答法不能只是“一個是線程安全的一個不是”而是要說清楚ConcurrentHashMap用CASsynchronized鎖粒度更細(xì)鎖的是桶位而不是整個數(shù)組從而提升并發(fā)度。當(dāng)時筆試選擇題里也有一道類似的判斷我在這里拿了分但同考場有人因為沒寫JDK8的紅黑樹閾值被扣了分。這張卷子對HashMap的追問深度說明它不是按“面試背誦題”來出而是按“你真的寫過Java代碼并處理過并發(fā)問題”來出。2.2 線程池參數(shù)問答看似基礎(chǔ)實處見功底另一道讓我印象深的題是一道選擇題加一道簡答的組合選擇項是“Java線程池中corePoolSize、maximumPoolSize、workQueue三者如何配合當(dāng)任務(wù)數(shù)超過核心線程數(shù)時新任務(wù)先入隊列還是先創(chuàng)建新線程”正確答案是當(dāng)提交任務(wù)時如果運行線程數(shù)小于corePoolSize創(chuàng)建新線程否則嘗試把任務(wù)放入workQueue如果隊列滿了且運行線程數(shù)小于maximumPoolSize創(chuàng)建新線程如果隊列滿了且線程數(shù)已經(jīng)達(dá)到maximumPoolSize走拒絕策略。很多人這里記反了以為是先創(chuàng)建線程到最大值再入隊順序不對面對突發(fā)流量時的資源走勢就完全不一樣。簡答部分問的是“shutdown()和shutdownNow()的區(qū)別”。shutdown會停止接收新任務(wù)隊列里的任務(wù)繼續(xù)執(zhí)行完shutdownNow會嘗試中斷正在執(zhí)行的任務(wù)并且返回尚未執(zhí)行的任務(wù)列表。我當(dāng)時還補充了一句調(diào)用shutdown之后配合awaitTermination可以在業(yè)務(wù)線程里等待線程池完全終止避免JVM主線程提前退出導(dǎo)致任務(wù)沒跑完。這個補充屬于實操經(jīng)驗普通教科書不太會寫據(jù)我后來和同學(xué)交流這個點讓卷面加分不少。2.3 網(wǎng)絡(luò)與操作系統(tǒng)數(shù)據(jù)鏈路視角的考察網(wǎng)絡(luò)部分的題目也比較典型。單選題里出現(xiàn)了“TCP建立連接為什么需要三次握手而不是兩次”這類題的本質(zhì)是確認(rèn)雙方的收發(fā)能力。第一次握手服務(wù)端確認(rèn)了客戶端的發(fā)送能力第二次握手客戶端確認(rèn)了服務(wù)端的接收和發(fā)送能力第三次握手服務(wù)端確認(rèn)了客戶端的接收能力。只握兩次服務(wù)端無法確認(rèn)客戶端是否具備接收能力這會導(dǎo)致半連接狀態(tài)下資源白白占用。還有一道操作系統(tǒng)的題問的是進程與線程的差別以及線程共享哪些資源。常規(guī)答法進程是資源分配的基本單位線程是CPU調(diào)度的基本單位同一進程內(nèi)的線程共享地址空間、打開的文件表、全局變量各自獨立的包括棧、寄存器狀態(tài)、程序計數(shù)器。這道題沒什么難點但它出現(xiàn)在“后端大數(shù)據(jù)”的卷子里有另一層含義大數(shù)據(jù)框架里進程模型和線程模型是兩套體系比如HDFS的NameNode是進程級服務(wù)而Spark Task是在Executor進程內(nèi)以線程方式調(diào)度搞清楚這個關(guān)系對理解資源隔離有直接幫助。2.4 我的丟分點一個不常見的HTTP狀態(tài)碼這里必須分享一個丟分教訓(xùn)。有一道多選題給了好幾個HTTP狀態(tài)碼問哪些屬于重定向狀態(tài)碼。我選了302和307漏了303和304結(jié)果丟分。這不是“不會”而是對狀態(tài)碼的邊界不熟。304 Not Modified本質(zhì)上是客戶端緩存命中的響應(yīng)歸類上屬于重定向類因為服務(wù)器并不返回響應(yīng)體而是告訴客戶端“用你緩存里的版本”。這個坑這幾年在很多技術(shù)社區(qū)里也被反復(fù)討論所以放在這里重點提醒刷題不要只盯大熱點冷門狀態(tài)碼、端口號、默認(rèn)配置這類容易被輕視的知識點在校招筆試?yán)锬芷鸬胶艽蟮暮Y人作用。觸寶這批卷子的選擇題本身不算難但覆蓋面廣任何一個領(lǐng)域的“半瓶水”都會在這里現(xiàn)形。3. 大數(shù)據(jù)專題HDFS、Spark與數(shù)據(jù)傾斜的標(biāo)準(zhǔn)化答案3.1 HDFS寫入全流程與副本放置策略大數(shù)據(jù)專題的簡答題第一道就是經(jīng)典的“描述HDFS文件的寫入過程”。這道題今天依然是絕大多數(shù)后端大數(shù)據(jù)校招的必考題我把當(dāng)時答題的框架寫在這里客戶端向NameNode發(fā)起寫請求NameNode檢查權(quán)限、文件是否已存在以及目錄結(jié)構(gòu)是否合法。通過檢查后NameNode在內(nèi)存中創(chuàng)建文件元數(shù)據(jù)記錄并返回可以寫入的DataNode列表??蛻舳税盐募凑諌K大小默認(rèn)128MB切分第一個塊的寫入過程是這樣的客戶端從返回的DataNode列表中選出第一個節(jié)點建立TCP連接將數(shù)據(jù)包以流水線方式依次傳給下一節(jié)點。第一個DataNode把數(shù)據(jù)落盤并傳給第二個第二個落盤再傳給第三個。每寫入一個chunk默認(rèn)512字節(jié)客戶端會計算校驗和隨數(shù)據(jù)一起傳輸DataNode校驗后存儲最終所有副本寫完后DataNode向客戶端發(fā)送確認(rèn)客戶端再通知NameNode更新元數(shù)據(jù)。答完流程之后我還補充了副本放置策略第一個副本放在客戶端所在節(jié)點的本地磁盤如果客戶端不在集群內(nèi)則隨機挑選一個負(fù)載不高的節(jié)點第二個副本放在不同機架的節(jié)點第三個副本與第二個放在同機架的不同機器上其余副本繼續(xù)隨機選擇。這樣做的平衡點是既能容忍機架級故障又不會因為跨機架寫太多數(shù)據(jù)導(dǎo)致網(wǎng)絡(luò)帶寬消耗過大。這道題的考察意圖不只是讓學(xué)生背流程而是觀察答題者有沒有工程視角。我當(dāng)時加了“如果最后一個Block小于128MB不會額外占用一個完整塊大小只需實際大小”這個細(xì)節(jié)就比泛泛而談的人多了一層可信度。3.2 計算引擎之辯MapReduce與Spark的思維差異簡答題的第二題是“MapReduce和Spark在計算模型上的主要區(qū)別各自適用于什么場景”。這個問題的答題質(zhì)量直接反映一個人是不是真的用離線計算處理過數(shù)據(jù)而不僅僅是看過概念。我的答題思路從三方面展開。執(zhí)行模型上MapReduce每個任務(wù)都有固定的Map-Shuffle-Reduce階段中間結(jié)果必須落盤到HDFS容錯依賴落盤恢復(fù)Spark把計算過程構(gòu)造成DAG中間結(jié)果優(yōu)先存內(nèi)存內(nèi)存不足才溢寫磁盤迭代類作業(yè)不需要每次都刷盤所以小迭代任務(wù)的速度優(yōu)勢明顯。調(diào)度粒度方面MapReduce以進程為單位每次啟動JVM開銷很大Spark以線程為單位在Executor內(nèi)部調(diào)度Task減少了資源啟動的損耗。數(shù)據(jù)處理模型上MapReduce是典型的兩階段批處理而Spark除了批處理還通過RDD/DataFrame統(tǒng)一了批流兩類計算語義Spark Streaming用微批的方式模擬實時計算。我還特意提了一個“適合場景”的觀點如果集群主要跑T1的定時報表任務(wù)量穩(wěn)定MapReduce的穩(wěn)定性和容易排查的特點足夠用但如果需要反復(fù)迭代、機器學(xué)習(xí)的特征計算、或者需要更快地響應(yīng)臨時分析需求Spark更合適。這個觀點也許沒有標(biāo)準(zhǔn)答案那么“正確”但能體現(xiàn)你真的思考過選型問題。據(jù)說后來我拿到的面試機會里面試官對這段回答印象比較深問了好幾句基于場景的討論。3.3 數(shù)據(jù)傾斜一道看似簡單卻勸退很多人的場景題簡答題里還有一道題干大概是“一個Spark任務(wù)在groupBy某個字段時某個key的量明顯多于其他key導(dǎo)致整個任務(wù)卡在最后一個stage如何定位和處理”這是大數(shù)據(jù)面試的經(jīng)典話題當(dāng)時在線上筆試?yán)镆院喆痤}形式出現(xiàn)我是從定位和處理兩個角度分開寫的。定位方面我寫了三個步驟先看Spark UI里各個Task的Shuffle Read大小傾斜的任務(wù)通常表現(xiàn)為某幾個Task讀到的數(shù)據(jù)量是其他Task的幾十倍再看Stage的劃分確認(rèn)傾斜發(fā)生在Shuffle之后最后確認(rèn)傾斜的key是什么可以通過在代碼里對key做count或直接在Spark UI日志里看到記錄數(shù)異常的Task編號對應(yīng)的數(shù)據(jù)特征。處理方面我提供了三種策略。第一是兩階段聚合對傾斜的key加一個隨機前綴先做局部聚合再去掉前綴做全局聚合第二是廣播優(yōu)化當(dāng)小表數(shù)據(jù)量不太大時比如幾十MB把維度表用broadcast join廣播出去避免Shuffle帶來的大key問題第三是過濾異常key如果傾斜的數(shù)據(jù)是無效數(shù)據(jù)比如空值、默認(rèn)值、爬蟲標(biāo)識可以在不影響業(yè)務(wù)口徑的前提下先過濾掉。我還補充了“如果傾斜是因為數(shù)據(jù)本身業(yè)務(wù)特性導(dǎo)致的加鹽法最穩(wěn)如果只是小表join大表優(yōu)先考慮廣播”。這道題不要求寫出完整代碼但能用工程語言把定位路徑和處理手段講清楚的人不多。很多人的答案只有“加隨機數(shù)”四個字缺少定位思路和適用邊界也就是所謂“知其然不知其所以然”。這種區(qū)分度就是筆試高分的分水嶺。4. 三道在線編程題邊界條件比算法思想更考驗人4.1 編程題一合并兩個有序數(shù)組題目大意給定兩個有序整數(shù)數(shù)組nums1和nums2nums1的長度為mn前m個元素是有效內(nèi)容nums2的長度為n把nums2合并進nums1結(jié)果仍有序要求空間復(fù)雜度O(1)。這是一個雙指針從后往前走的經(jīng)典題。很多人的第一反應(yīng)是新建一個數(shù)組再sort或者用額外O(mn)的空間去合并但題目明確限制了空間。當(dāng)時的實現(xiàn)思路是從nums1的m-1位置和nums2的n-1位置開始往前遍歷誰大誰就放到nums1的末尾從mn-1位置往前填直到nums2全部被放完。這樣不會覆蓋nums1還沒處理到的有效數(shù)據(jù)。public void merge(int[] nums1, int m, int[] nums2, int n) { int p1 m - 1, p2 n - 1, p m n - 1; while (p2 0) { if (p1 0 nums1[p1] nums2[p2]) { nums1[p--] nums1[p1--]; } else { nums1[p--] nums2[p2--]; } } }這道題考察的核心不是雙指針本身而是“剩余數(shù)據(jù)處理”的邊界條件。while循環(huán)退出條件是p2 0代表nums2已經(jīng)被完全合并nums1剩下的那些前半段元素天然有序不需要額外處理。很多人寫while(p1 0 p2 0)然后漏掉單獨處理p2剩余的情況就會在部分測試用例上報錯。我當(dāng)時提交后自己又檢查了一遍邊界并寫了兩個用例去驗證一個是nums1為空一個是nums2為空。這種測試習(xí)慣比代碼本身更能提升得分。4.2 編程題二找出數(shù)組中缺失的最小正整數(shù)題目大意給定一個未排序的整數(shù)數(shù)組找出其中沒有出現(xiàn)的最小的正整數(shù)。要求時間復(fù)雜度O(n)空間復(fù)雜度O(1)。這道題在今天已經(jīng)算高頻題但2017年出現(xiàn)在校招卷子里區(qū)分度比較明顯。正確思路是原地哈希數(shù)值為i的元素應(yīng)該放到下標(biāo)i-1的位置。遍歷數(shù)組當(dāng)當(dāng)前元素在[1, n]范圍內(nèi)且不在正確位置上時與目標(biāo)位置交換。交換完成后再遍歷一次第一個不滿足nums[i] i 1的位置就是缺失的最小正整數(shù)如果全部滿足返回n 1。public int firstMissingPositive(int[] nums) { int n nums.length; for (int i 0; i n; i) { while (nums[i] 0 nums[i] n nums[nums[i] - 1] ! nums[i]) { int tmp nums[nums[i] - 1]; nums[nums[i] - 1] nums[i]; nums[i] tmp; } } for (int i 0; i n; i) { if (nums[i] ! i 1) return i 1; } return n 1; }這里最關(guān)鍵的一段是while里的三個條件只處理[1,n]范圍內(nèi)的數(shù)需要等待當(dāng)前數(shù)還沒放到正確位置時持續(xù)交換如果目標(biāo)位置已經(jīng)有相同值說明重復(fù)不進入交換避免死循環(huán)。這道題我見過很多同學(xué)在“重復(fù)元素導(dǎo)致死循環(huán)”上卡住筆試現(xiàn)場時間緊迫更容易忽略。建議所有備戰(zhàn)校招的人遇到數(shù)組原地交換類的題第一件事就是問自己兩個問題會不會越界會不會因為重復(fù)值死循環(huán)4.3 編程題三字符串形式的IP地址合法性校驗題目大意給定一個字符串判斷它是否是合法的IPv4地址。要求考慮前導(dǎo)零、數(shù)字范圍、非法字符等情況。這類字符串處理的題不算難但邊界條件極其細(xì)致特別適合筆試篩選。核心判斷邏輯是按點分割后必須正好4段每段不能為空每段只能包含數(shù)字每段長度不超過3轉(zhuǎn)換成int后在0到255之間如果長度大于1不能以0開頭也就是不能有前導(dǎo)零。我當(dāng)時先講清楚思路再寫循環(huán)判斷。當(dāng)時我忽略了一個點字符串里的空白字符。如果輸入是192.168.1.1 末尾多了一個空格很多語言里split之后最后一段是1 直接轉(zhuǎn)int會報異常。雖然題目未必包含這個測試用例但如果能主動處理并寫出“先trim再分割”的細(xì)節(jié)會顯得經(jīng)驗更足。我在做題時加了一句注意雖然浪費了三十秒但為后面說明邊界處理思路做好了鋪墊。4.4 做題順序與時間分配的實操體會三題編程題我用了大約50分鐘前兩題各15分鐘IP校驗20分鐘。整體耗時比預(yù)想多原因是在IP校驗上反復(fù)檢查了邊界。這里分享一個實戰(zhàn)經(jīng)驗在線筆試的編程題千萬不要在一道題上死磕超過30分鐘。如果30分鐘還沒通過全部用例先提交部分分回頭再調(diào)。觸寶這套卷子總分里編程題占比較高但拿到部分分依然比零分強很多。我當(dāng)時遇到一道排序題因為時間不夠只寫了思路靠簡答題的完整答案把總分拉了上來。另一條心得是編譯器通常沒有自動構(gòu)造函數(shù)提示很多考題平臺也不允許import額外的包。平時練習(xí)時一定要適應(yīng)“裸寫代碼”的節(jié)奏不要依賴IDE的智能提示。Map、List這些常用類的API必須手寫熟練否則在線筆試會很吃虧。5. 場景設(shè)計題從埋點到報表的離線數(shù)據(jù)鏈路設(shè)計5.1 題干還原與隱含的考察點最后的大題是純場景設(shè)計題干大概是這樣的“觸寶輸入法App會記錄用戶的行為日志、詞庫糾錯日志、崩潰日志等多種數(shù)據(jù)目前日均產(chǎn)生約10億條日志需要支撐運營后臺每天查看熱詞榜、用戶留存、地域分布等報表同時支持小時級的趨勢變化。請設(shè)計一套數(shù)據(jù)采集、存儲、計算、展示的完整鏈路并說明關(guān)鍵環(huán)節(jié)的選型理由?!边@道題是全卷最好的一道題因為沒有任何標(biāo)準(zhǔn)答案。它的考察點不只是一個技術(shù)棧而是你能不能把從客戶端到報表端的全鏈路串起來并且在關(guān)鍵環(huán)節(jié)給出合理取舍。而且題干特意給了“小時級趨勢變化”這個約束這意味著不能只設(shè)計一個純T1的離線數(shù)倉還需要考慮小時級任務(wù)如何調(diào)度、結(jié)果如何加速。5.2 我當(dāng)時的答題框架我的答案分五層采集層、緩存與傳輸層、存儲層、計算層、服務(wù)與展示層并畫了一條鏈路客戶端日志 - Nginx統(tǒng)一接入 - Kafka - Flume落HDFS - Hive數(shù)倉分層 - Spark SQL跑計算 - 結(jié)果入MySQL/Redis - 報表平臺查詢。先寫采集層。客戶端日志統(tǒng)一通過HTTP接口上報到Nginx集群日志格式約定為JSON方便后續(xù)解析。這里選Nginx是因為它抗高并發(fā)、配置簡單而且可以作為第一層負(fù)載入口。Nginx的access_log不能直接作為業(yè)務(wù)日志管道所以業(yè)務(wù)日志從Nginx轉(zhuǎn)發(fā)到Kafka而不是寫本地文件再采集這樣能減少一個環(huán)節(jié)。然后寫Kafka的作用。Kafka主要做削峰和異步緩沖。日志上報具有明顯的晝夜波峰直接寫HDFS的話落盤壓力會隨流量波動而Kafka能穩(wěn)定承接讓下游消費者按自己的速率拉取數(shù)據(jù)。分區(qū)數(shù)按Topic的數(shù)據(jù)量來估算保證每個分區(qū)消費吞吐量合理。同時Kafka還能作為多下游復(fù)用的數(shù)據(jù)源比如實時告警的Flink任務(wù)和離線Hive任務(wù)可以消費同一個Topic互不干擾。存儲層用的是HDFS按天和按小時二級分區(qū)比如/log_date20250101/hour10/這樣的目錄結(jié)構(gòu)。分區(qū)的好處是查詢時能快速裁剪數(shù)據(jù)不用掃描全量。Hive表通過外部表方式關(guān)聯(lián)HDFS目錄業(yè)務(wù)數(shù)據(jù)模型在Hive里分為三層ODS層原樣存儲日志原始數(shù)據(jù)DWD層做清洗和維度統(tǒng)一比如統(tǒng)一設(shè)備ID、識別用戶ID去掉無效字段ADS層匯聚應(yīng)用層指標(biāo)如每日熱詞TopN、日活、留存率。分層的好處是每層職責(zé)單一ODS不動原始數(shù)據(jù)DWD改壞了不影響源頭ADS直接面向業(yè)務(wù)查詢。計算層主用Hive和Spark SQL。日級別的報表用Hive定時任務(wù)即可小時級別的趨勢指標(biāo)用Spark SQL因為它的執(zhí)行速度更快能保證小時任務(wù)在每小時的05分前后產(chǎn)出不影響運營查看。計算時采用增量計算狀態(tài)累積的策略即只算新到一個小時的數(shù)據(jù)再與歷史累計狀態(tài)合并避免每天重復(fù)跑全量。服務(wù)與展示層是MySQL存維度表、配置表和報表結(jié)果Redis存熱詞榜、實時看板這類需要秒級返回的查詢。報表平臺的后端接口從Redis或MySQL讀取結(jié)果數(shù)據(jù)提供日環(huán)比、周同比等對比邏輯。MySQL的表結(jié)構(gòu)按指標(biāo)維度設(shè)計行數(shù)不會太大不需要分庫分表但必須加索引。5.3 這道題的隱藏加分點與我的補充我在答完主鏈路后還補了三個容易被忽視的環(huán)節(jié)數(shù)據(jù)質(zhì)量監(jiān)控、精確去重方案、任務(wù)失敗重跑機制。數(shù)據(jù)質(zhì)量監(jiān)控的思路是每天計算任務(wù)結(jié)束后拿當(dāng)天的總?cè)罩玖颗c昨天的同時段數(shù)據(jù)做環(huán)比如果波動超過50%說明上游采集可能出現(xiàn)問題需要馬上告警。另外在DWD層加一張數(shù)據(jù)質(zhì)量校驗表記錄每個分區(qū)記錄數(shù)、空值比例、異常值比例一旦指標(biāo)異常報表展示前就攔截。這個點許多沒做過生產(chǎn)環(huán)境數(shù)據(jù)的同學(xué)是想不到的。精確去重是我單獨強調(diào)的。日活的UV統(tǒng)計不能用簡單的count(distinct device_id)刷全表因為數(shù)據(jù)量大時開銷太高。可以每天把當(dāng)天的device_id字段按天去重后寫入bitmap或者用Spark的approx_count_distinct做近似去重誤差控制在1%以內(nèi)。如果業(yè)務(wù)口徑要求嚴(yán)格的精確UV再用bitmap方案如果只關(guān)心量級趨勢用HyperLogLog近似算法性價比更高。這個取舍本身就是大數(shù)據(jù)工程里常見的“精確性換性能”的權(quán)衡。任務(wù)失敗重跑和調(diào)度依賴我提到用調(diào)度平臺管理事件驅(qū)動式任務(wù)依賴ODS導(dǎo)入完成后才觸發(fā)DWD清洗DWD完成后才觸發(fā)ADS指標(biāo)計算不會出現(xiàn)上游還沒跑完下游就開跑的空數(shù)據(jù)情況。失敗后要支持指定分區(qū)重跑而不是全表重算這對資源消耗和SLA都重要。這道題最終得分取決于綜合表達(dá)鏈路完整性是第一檔選型取舍是第二檔隱藏問題意識是第三檔。大多數(shù)人能畫出“日志-Kafka-HDFS-Hive-報表”的主干但能把監(jiān)控、去重、調(diào)度依賴補全的人很少而這幾個點恰好是日常工作里每天都要面對的事情。6. 從這份試卷反推備考策略幾年后回看依然有效的判斷6.1 從試卷看“后端大數(shù)據(jù)”崗位到底要什么人復(fù)盤完所有題目可以直接總結(jié)出這個崗位的能力模型第一Java基礎(chǔ)必須扎實HashMap、并發(fā)、網(wǎng)絡(luò)這些通用后端知識不能被問倒第二要熟悉Hadoop生態(tài)的常用組件原理尤其是HDFS和Spark這類每天打交道的系統(tǒng)得清楚它們的運行機制而不是只會調(diào)用API第三動手編碼能力必須有保障手寫數(shù)組、字符串、哈希相關(guān)的算法題不能發(fā)怵第四要有真實的數(shù)據(jù)工程體感知道日志從產(chǎn)生到展示會經(jīng)過哪些環(huán)節(jié)哪些環(huán)節(jié)容易出問題。這套能力模型不只是觸寶一家如此。我后來投過不少做用戶增長、內(nèi)容分發(fā)、智能硬件數(shù)據(jù)的公司筆試題的框架基本都是“后端基礎(chǔ)大數(shù)據(jù)原理算法題場景設(shè)計”四件套差別只在深度和選型偏好上。所以備考重點不在于猜測某家公司會出什么原題而在于把這份能力模型里的短板補齊。6.2 給正在備考校招的同學(xué)幾條具體建議第一Java基礎(chǔ)用“高頻問答清單”來查漏。HashMap、JVM內(nèi)存區(qū)域、類加載、線程池參數(shù)、synchronized與ReentrantLock的區(qū)別這些是后端筆試的骨干內(nèi)容每天花半小時針對每個主題試著不看資料講一遍能發(fā)現(xiàn)自己記憶里含糊的地方。第二Hive SQL一定要寫得熟。實際筆試?yán)锊粫苯映觥罢垖懗鲆粭lSQL”但場景題里大量涉及指標(biāo)的口徑拆解如果你能順手寫出“近7日每日活躍用戶數(shù)”這類SQL在面試?yán)镆彩羌臃猪棥5谌謱懘a練習(xí)時刻意練習(xí)“先寫邊界條件再寫主邏輯”的習(xí)慣很多筆試題因為邏輯正確但遺忘空數(shù)組、空字符串、首尾空格、負(fù)數(shù)等邊界情況導(dǎo)致不能全部通過非??上?。第四準(zhǔn)備一份“場景設(shè)計答題模板”不光是背下來而是真正理解每一層的作用然后針對不同業(yè)務(wù)去套用和變化。6.3 我的個人體會這批筆試過去好幾年了但每次有師弟師妹問我校招怎么準(zhǔn)備后端大數(shù)據(jù)方向我都會翻出這份復(fù)盤給他們看。不是因為它有多難而是它代表了一類典型的、務(wù)實的校招筆試風(fēng)格不考偏題怪題不搞腦筋急轉(zhuǎn)彎每一道題都在考察“你平時寫代碼、搭系統(tǒng)、跑數(shù)據(jù)時有沒有認(rèn)真思考過為什么”。我至今還記得在答場景設(shè)計題時我腦中回放的是自己課程設(shè)計里搭數(shù)倉時踩過的坑忘了對ODS數(shù)據(jù)做校驗結(jié)果報表里出現(xiàn)了翻倍的日活數(shù)據(jù)排查了整整一天。那次踩坑最終成了筆試答案里“數(shù)據(jù)質(zhì)量監(jiān)控”那個分點的來源。很多經(jīng)驗?zāi)阋詾闆]用會在將來某個時刻突然變得重要。校招筆試在短期內(nèi)看是一場競爭長期看更像是對過去積累的一次檢驗。真正讓你拿到offer的不是考前的突擊而是你平時寫每一行代碼、處理每一份數(shù)據(jù)時留下的思考深度。