跳至主要內容
科技 科普

先查快取再查資料庫,這段程式碼到底在防什麼:一段面試題教你的並發思維課

一段 V2EX 上的面試題程式碼引發討論,本文用科普角度拆開本地快取的執行緒安全設計,說明雙重檢查與分段鎖的思維結構。

YIM NEWS 編輯台 閱讀約 5 分鐘

一段不到三十行的 Java 程式碼,九則回覆,一千多次瀏覽。2026 年 9 月 11 日,開發者社羣 V2EX 上有人貼出一道流傳多年的面試題,問的不是答案,而是「你大腦裡怎麼分析這類問題」。這個提問方式,反而讓這段老題目值得重新講一次。

先把場景講清楚。這段程式碼處理的是電商系統裡最日常的需求:根據店鋪 ID 查店鋪資料。邏輯是先查本地快取,快取裡沒有就去資料庫撈,撈到再放回快取。聽起來就像你在家找不到鑰匙,先翻抽屜,抽屜沒有再去門口找,找到之後順手放回抽屜,下次就不用再跑一趟。

問題出在「家裡不止你一個人」。

單機版的排隊難題

想像一家小店,櫃檯後面貼著一張常用的供應商電話清單。新來的店員找不到電話時,會打電話去問總公司。這件事本身沒問題,問題是某個促銷瞬間,五十個店員同時都需要同一通電話,而清單剛好還沒抄上去。於是五十個人同時拿起話筒打給總公司。總公司那一頭,就是資料庫。

這就是工程術語裡的「快取擊穿」。單一熱門資料在快取裡缺席的瞬間,大量請求同時穿透到資料庫,平常安然無恙的系統在這一秒可能直接被壓垮。

那段被討論的程式碼,用兩個動作回應這個場景。第一個動作是「同一個店鋪 ID,大家一起排同一條隊」。程式裡有一個專門存放鎖物件的 Map,每個店鋪 ID 對應一顆鎖。要查同一間店鋪的執行緒,全都在同一顆鎖前排隊;查不同店鋪的執行緒,各排各的隊,互不幹擾。這比整家店只開一個櫃檯(全域鎖)有效率得多,也把「只保護真正需要保護的那一小段」這個原則落實到程式裡。

第二個動作是「進門後再確認一次」。排到隊伍前面的執行緒,不急著打給總公司,而是再看一眼清單。因為前一個排隊的人可能剛剛已經抄上去了,你直接用就好。這就是所謂的雙重檢查:第一次檢查在鎖外,讓絕大多數時候根本不用排隊;第二次檢查在鎖內,防止排隊期間別人已經把活幹完。

理解到這裡,整段程式碼就不再是背誦題,而是一套可以套用到任何並發問題的分析框架:先問「哪些資料會被多人同時碰」,再問「同時碰會出什麼錯」,最後問「鎖該鎖多細、鎖在哪一段」。

為什麼討論串裡有人說「多此一舉」

貼文下的回覆裡,有幾位開發者提出同一個疑問:Java 的 ConcurrentHashMap 本身就提供 computeIfAbsent 方法,一行就能做到「沒有就計算並放入」,為什麼要手寫鎖、手寫雙重檢查?

這個質疑有其道理,也正好揭示了並發設計裡最容易被忽略的細節。computeIfAbsent 對「Map 本身的操作」是原子的,它保證同一個 key 不會被重複計算。但這段面試題的情境裡,鎖內要執行的是一次資料庫查詢,一個可能耗時幾十毫秒甚至更久的網路往返。

在部分 JDK 版本與使用情境下,把長時間的遠端呼叫塞進 computeIfAbsent 的計算函式,等於讓 Map 內部為了維持一致性而長時間佔住資源,其他 key 的操作也可能被拖累。更根本的一點是,面試題想檢驗的從來不是「你知不知道有這個 API」,而是你能不能自己推導出「為什麼需要鎖、鎖誰、鎖多久」這整條推理鏈。工具會換,推理鏈不會。

這也是原提問者真正想聊的事。他說自己「拿著答案分析過程」,多年前就看過這題,今天再讀卻還是喫力。這種感覺許多工程師都不陌生:面試前背了結論,進了職場遇到新的並發問題,腦中的模板對不上現場。討論串裡一位回覆者的做法值得參考:先把業務邏輯用最天真的方式寫出來,再回頭找「如果兩個執行緒同時跑到這一行會怎樣」的破口,找到破口才去想鎖。先有威脅模型,再談防禦手段,順序反過來就會變成背題。

回到日常:這些判斷用在哪裡

就算你不寫 Java,這套思維也到處適用。前端防表單重複送出,用的是同一種「鎖住同一個動作」的想法;資料庫的樂觀鎖、分散式系統裡的分散式鎖,處理的是同一類問題在不同尺度的版本。差別只在於:單機用語言內建的鎖,跨機器就要靠 Redis 或資料庫本身來充當那顆鎖。概念同源,實作換殼。

另外兩個從這段討論延伸出來的實務判斷也值得記下。其一,本地快取存的是單一行程的記憶體,如果你有多臺伺服器,每臺各自擊穿一次,問題只是被分散而沒有被解決,這時要考慮的是跨機制的協調或提前預熱熱點資料。其二,快取裡的資料會過期、會與資料庫不一致,鎖只解決「同時讀」的問題,不解決「讀到舊的」的問題,那是另一道題。

程式語言的鎖會過時,框架的 API 會改名,但「找出共用狀態、識別競爭窗口、把鎖的範圍縮到最小」這三步,是並發世界裡少數不會退流行的東西。一段面試題能在社羣裡被反覆拿出來討論,原因大概就在這裡:它考的不是記憶,是你腦中那張結構圖畫得清不清楚。

#java+並行程式設計#concurrenthashmap+快取架構#軟體工程+面試文化