面試高頻考點:冪等設(shè)計、實時榜單與高并發(fā)防作弊)
簡介這是一套面向知識競賽組織者與開發(fā)者的在線答題系統(tǒng)源碼資源基于VC6.0與MFC框架開發(fā)配套Access數(shù)據(jù)庫適合需要搭建競賽平臺或進行課程設(shè)計、二次開發(fā)的技術(shù)人員參考。壓縮包共61個文件約7.68MB以cpp與h源文件為核心另有obj、pdb、ilk等編譯中間文件以及mdf、ldf數(shù)據(jù)庫文件、bmp與ico界面素材和dsp、dsw工程文件結(jié)構(gòu)完整可直接編譯運行。系統(tǒng)覆蓋試題的添加、刪除與修改參賽人員信息錄入在線答題計時與自動評分并支持按答對數(shù)量、答題速度等維度生成動態(tài)排名同時包含通知消息、數(shù)據(jù)統(tǒng)計與安全隱私等模塊。已有395人學(xué)習(xí)下載讀者可從中獲取完整的競賽系統(tǒng)實現(xiàn)方案、數(shù)據(jù)庫腳本與界面設(shè)計思路便于理解MFC與ADO數(shù)據(jù)庫訪問的具體用法快速完成系統(tǒng)維護、功能擴展或二次開發(fā)。1. 知識競賽系統(tǒng)面試從答題到排名考官到底在盯什么很多人以為知識競賽系統(tǒng)的面試就是問“怎么存題目”“怎么算分?jǐn)?shù)”真坐到對面才發(fā)現(xiàn)考官根本不關(guān)心你能不能把題庫表建出來。他們盯的是三件事答題過程怎么保證不崩、排名怎么保證不飄、高并發(fā)下怎么保證不丟分。這三件事對應(yīng)的是消息隊列、緩存一致性、分布式鎖、冪等設(shè)計這些硬考點而不是增刪改查。一份能拿高分的備考筆記必須先把考點分布摸清楚答題鏈路占四成排名與榜單占三成防作弊與冪等占兩成剩下的才是基礎(chǔ)數(shù)據(jù)模型。適合已經(jīng)寫過項目、但一被追問“為什么”就卡殼的人。下面按模塊拆開講每道題都給答題思路照著練就能用。2. 答題鏈路高頻考點從提交到判分每一步都可能被追問2.1 答題提交接口的冪等設(shè)計面試官最愛問的第一道題用戶手抖點了兩次提交或者網(wǎng)絡(luò)重試導(dǎo)致同一份答案發(fā)了兩遍你怎么保證不重復(fù)計分這題考的是冪等不是“加個鎖”就完事。答題思路分三層說。第一層客戶端生成一個全局唯一的提交令牌隨答案一起發(fā)到服務(wù)端服務(wù)端用 Redis 的SET key value NX EX做占位占位成功才繼續(xù)處理失敗直接返回上一次的結(jié)果。第二層數(shù)據(jù)庫層面對(user_id, question_id, exam_id)建唯一索引作為最后一道防線防止緩存穿透后重復(fù)寫入。第三層如果判分是異步的消息消費端也要用同一個令牌做去重保證“至少一次投遞”不會變成“至少一次計分”。import redis import hashlib r redis.Redis() def submit_answer(user_id, exam_id, question_id, answer, token): # 用提交令牌做冪等鍵NX 保證只有第一次能占位成功 idem_key fidem:{exam_id}:{user_id}:{token} # EX 設(shè)置過期時間防止死鍵堆積 ok r.set(idem_key, 1, nxTrue, ex300) if not ok: # 已經(jīng)處理過直接返回緩存里的結(jié)果 return r.get(fresult:{idem_key}) # 真正落庫判分 score judge(question_id, answer) r.set(fresult:{idem_key}, score, ex300) return score這段代碼的關(guān)鍵在nxTrue和ex300。nx保證原子性占位ex防止用戶一直不重試導(dǎo)致鍵永久占用。注意判分邏輯要放在占位成功之后否則占位失敗也會白算一次。面試時如果只答“用 Redis 做冪等”考官會追問“Redis 掛了怎么辦”這時候要補數(shù)據(jù)庫唯一索引兜底才算答完整。2.2 判分服務(wù)的同步與異步選擇第二道高頻題判分是同步返回還是異步返回這題沒有標(biāo)準(zhǔn)答案但答錯方向直接暴露你沒想過流量。答題思路客觀題、題量小、要求即時出分走同步接口里直接算響應(yīng)控制在 200ms 內(nèi)。主觀題、題量大、或者有復(fù)雜規(guī)則比如多選漏選給一半分走異步提交后返回一個task_id客戶端輪詢或走長連接拿結(jié)果。異步方案要講清楚消息隊列選型、消費失敗重試、以及結(jié)果回寫后怎么通知前端。面試官最想聽的是你對“用戶等多久會煩躁”的判斷。一般超過 1.5 秒的等待用戶就會懷疑系統(tǒng)卡了。所以同步判分只做輕量計算重邏輯一律異步。如果考官追問“異步怎么保證用戶看到分?jǐn)?shù)”答提交后前端展示“判分中”用task_id每 500ms 輪詢一次最多輪詢 10 次超時提示“稍后在成績頁查看”。這樣既不讓用戶干等也不給服務(wù)端壓力。2.3 答題過程中的斷線續(xù)答第三道題很實戰(zhàn)用戶答到第 8 題突然斷網(wǎng)重新進來怎么恢復(fù)進度這題考的是狀態(tài)存儲和恢復(fù)策略。答題思路每答一題客戶端本地先存一份同時異步上報服務(wù)端。服務(wù)端用user_id exam_id作為 key存一個答題進度對象包含已答題號、已選答案、剩余時間?;謴?fù)時優(yōu)先讀服務(wù)端服務(wù)端沒有再用本地。注意剩余時間不能只靠客戶端算否則改本地時間就能作弊服務(wù)端要記錄開考時間戳恢復(fù)時用服務(wù)端時間重新計算剩余秒數(shù)。提示進度存儲別用關(guān)系庫一行行更新用 Redis Hash 存整個進度對象一次讀寫搞定還能順帶設(shè)置和考試時長一致的過期時間。3. 排名與榜單高頻考點實時榜、最終榜、并列名次怎么答3.1 實時排名的數(shù)據(jù)結(jié)構(gòu)選型排名題第一問實時榜單怎么實現(xiàn)很多人張口就是“數(shù)據(jù)庫 order by”考官一聽就知道沒做過高并發(fā)。答題思路用 Redis 的有序集合 ZSET。ZADD更新分?jǐn)?shù)ZREVRANGE取前 N 名ZREVRANK查某人排名全是 O(log N)。分?jǐn)?shù)相同時按提交時間早的排前面可以把分?jǐn)?shù)和時間戳拼成一個復(fù)合分值比如score * 10^10 - timestamp這樣分?jǐn)?shù)高的自然靠前同分時時間早的靠前。# 更新用戶分?jǐn)?shù)exam:1001:rank 是榜單鍵 ZADD exam:1001:rank 950 user:2001 # 取前 10 名WITHSCORES 帶分?jǐn)?shù) ZREVRANGE exam:1001:rank 0 9 WITHSCORES # 查 user:2001 的排名從 0 開始算 ZREVRANK exam:1001:rank user:2001參數(shù)說明ZADD的分?jǐn)?shù)是 double注意精度分?jǐn)?shù)差距小于 1e-10 會出問題所以分?jǐn)?shù)最好用整數(shù)。ZREVRANGE的起止索引是閉區(qū)間取前 10 是0 9。如果榜單要分頁用ZREVRANGE key start stop配合頁碼算偏移。面試時如果被問“ZSET 底層是什么”答跳表加哈希表跳表保證范圍查詢快哈希表保證 O(1) 查分?jǐn)?shù)。3.2 最終榜單與實時榜單的差異處理第二問考試結(jié)束后最終榜單怎么定實時榜和最終榜能一樣嗎答題思路不能一樣。實時榜追求快允許短暫不一致最終榜追求準(zhǔn)必須等所有判分完成、所有異步任務(wù)落庫后再生成。做法是考試結(jié)束后發(fā)一個“結(jié)算”消息消費端把所有答題記錄聚合算出最終分?jǐn)?shù)再寫入一個獨立的最終榜單鍵。最終榜單生成后加個標(biāo)記位前端讀榜時先看標(biāo)記位有最終榜就讀最終榜沒有才讀實時榜。面試官想聽的是你對“結(jié)算時機”的把控。如果判分有異步任務(wù)結(jié)算必須等所有任務(wù)完成可以用一個計數(shù)器記錄待完成任務(wù)數(shù)歸零時觸發(fā)結(jié)算。別小看這個細(xì)節(jié)很多考生答“考試結(jié)束就生成最終榜”考官會追問“異步判分還沒完怎么辦”這時候就露餡了。3.3 并列名次的處理邏輯第三問兩個人分?jǐn)?shù)一樣排名怎么算這題考的是業(yè)務(wù)理解和細(xì)節(jié)。答題思路先明確業(yè)務(wù)規(guī)則。常見兩種一種是并列都算第 5 名下一個是第 7 名另一種是打破并列按提交時間早的排前面。面試時先反問“業(yè)務(wù)上要求并列還是打破”再給方案。如果并列ZSET 里分?jǐn)?shù)相同ZREVRANK返回的排名是任意的需要額外查同分人數(shù)來修正。如果打破并列就用前面說的復(fù)合分值。注意并列名次在發(fā)獎場景下很敏感第 5 名并列兩個獎品怎么發(fā)面試時主動提這個業(yè)務(wù)點考官會覺得你考慮得全。4. 防作弊與高并發(fā)易錯點這些題一錯就丟分4.1 易錯點一用數(shù)據(jù)庫自增做主鍵還沾沾自喜錯在哪答題記錄表用自增 ID高并發(fā)下自增鎖競爭嚴(yán)重插入變慢。為什么錯知識競賽的答題寫入是高頻操作自增主鍵在并發(fā)插入時會產(chǎn)生鎖等待QPS 上不去。怎么答對用雪花算法生成分布式 ID或者用user_id exam_id question_id做聯(lián)合主鍵避免單點自增。面試時如果被問“為什么不用自增”把鎖競爭和分庫分表擴展性兩點講清楚。4.2 易錯點二排行榜用數(shù)據(jù)庫 order by 還覺得沒問題錯在哪每次查榜都select * from score order by score desc limit 10數(shù)據(jù)量一大就慢查詢。為什么錯排序無法命中索引時走全表掃描幾千人同時看榜直接拖垮數(shù)據(jù)庫。怎么答對用 Redis ZSET 扛讀數(shù)據(jù)庫只做持久化榜單讀請求全部走緩存。如果考官追問“緩存和數(shù)據(jù)庫不一致怎么辦”答寫分?jǐn)?shù)時先寫數(shù)據(jù)庫再更新 ZSET用消息隊列保證最終一致讀榜只讀 ZSET。4.3 易錯點三判分邏輯寫在接口里還覺得挺清晰錯在哪判分規(guī)則和接口耦合改一個規(guī)則要動接口代碼回歸測試全量跑。為什么錯知識競賽的判分規(guī)則經(jīng)常變比如多選從“全對得分”改成“漏選得一半”硬編碼在接口里就是災(zāi)難。怎么答對把判分規(guī)則抽成策略模式每種題型一個策略類接口只負(fù)責(zé)調(diào)度。面試時補一句“規(guī)則配置化運營后臺可改”考官會高看一眼。4.4 易錯點四忽略答題時間的服務(wù)端校驗錯在哪剩余時間只在前端倒計時服務(wù)端不校驗用戶改本地時間就能多答題。為什么錯這是安全漏洞不是功能問題。怎么答對服務(wù)端記錄開考時間戳和考試時長每次提交答案時校驗當(dāng)前時間 - 開考時間 考試時長超時直接拒絕。如果考官問“網(wǎng)絡(luò)延遲導(dǎo)致提交晚了一秒怎么辦”答允許一個小的寬限期比如 3 秒寬限期內(nèi)提交仍有效但記錄異常供風(fēng)控排查。4.5 易錯點五消息隊列消費失敗直接丟棄錯在哪異步判分消息消費失敗沒有重試用戶分?jǐn)?shù)丟了。為什么錯消息隊列的“至少一次”語義要求消費端必須冪等且可重試直接丟棄等于數(shù)據(jù)丟失。怎么答對消費失敗進死信隊列人工介入或定時重試重試時用冪等鍵去重。面試時如果被問“重試多少次”答一般 3 次間隔遞增超過進死信隊列并告警。5. 答題技巧與押題思路一套能直接套用的答題框架面試知識競賽系統(tǒng)最怕的是東一榔頭西一棒子。我后來總結(jié)了一個四步框架每次遇到系統(tǒng)設(shè)計題都強制走一遍先問業(yè)務(wù)場景再定數(shù)據(jù)模型然后拆讀寫鏈路最后補異常和邊界。這套框架能覆蓋八成以上的追問。第一步問業(yè)務(wù)場景。別急著答技術(shù)方案先確認(rèn)考試規(guī)模、并發(fā)量、是否實時出分、有沒有防作弊要求。比如“這場考試是 100 人還是 10 萬人”直接決定你用不用消息隊列和分布式緩存。面試官拋出題目后你反問一句“大概什么量級”他會覺得你靠譜。第二步定數(shù)據(jù)模型。題庫表、答題記錄表、榜單表三張核心表先畫出來。答題記錄表用聯(lián)合唯一索引防重榜單表可以不存在數(shù)據(jù)庫里直接 Redis ZSET。這一步要講清楚每張表的讀寫比例題庫讀多寫少答題記錄寫多讀少榜單讀多寫少。第三步拆讀寫鏈路。寫鏈路提交答案 → 冪等校驗 → 判分 → 寫記錄 → 更新榜單。讀鏈路查題目 → 查進度 → 查榜單。每條鏈路標(biāo)出瓶頸點寫鏈路的瓶頸在判分和榜單更新讀鏈路的瓶頸在榜單查詢。瓶頸點就是你要加緩存、加隊列的地方。第四步補異常和邊界。斷線怎么辦、重復(fù)提交怎么辦、判分失敗怎么辦、榜單并列怎么辦、緩存掛了怎么辦。每個異常給一個兜底方案不用多復(fù)雜但要有。押題方向上優(yōu)先準(zhǔn)備三道題設(shè)計一個支持 10 萬人同時答題的知識競賽系統(tǒng)實時榜單怎么實現(xiàn)同分怎么處理答題提交的冪等和防作弊怎么做。這三道題練熟基本覆蓋核心考點。另外準(zhǔn)備一個自己項目里的翻車經(jīng)歷比如“當(dāng)時榜單用數(shù)據(jù)庫排序壓測直接崩了后來換 ZSET 才扛住”面試官對血淚經(jīng)驗比對標(biāo)準(zhǔn)答案更感興趣。提示答題時多用“我會先…再…最后…”的句式讓考官跟上你的思路。別一上來就堆技術(shù)名詞先講清楚為什么選這個方案再講怎么實現(xiàn)。從那以后我每次準(zhǔn)備面試都強制自己把每個系統(tǒng)設(shè)計題按這四步寫一遍寫完再對著鏡子講一遍講不順的地方就是沒想透的地方。希望幫到你。本文還有配套的精品資源點擊獲取