免費完整指南

檔案同步系統

設計檔案同步系統。請涵蓋File chunking strategy and content-hash based deduplication across users, Delta sync: rsync-style block diffing instead of re-uploading whole files, Metadata service (file tree, versions)...

00

練習檢查點

面試節奏保持精簡,讓頁面能把注意力花在真正的設計決策上。

  1. 01
    釐清範圍
  2. 02
    需求 + 規模
  3. 03
    API + 資料模型
  4. 04
    畫出架構
  5. 05
    深入探討
  6. 06
    取捨決策
01

形塑設計的需求

不要只是陳述需求,要主動問出來。每張卡把設計限制和一句你可以在畫架構前說出口的釐清問句配成一組。

功能需求

01單一檔案最大可以到多大?

使用者可從任何裝置上傳與下載,單檔最大 50 GB。

02同步是自動的嗎,離線編輯會怎樣?

檔案在所有裝置間自動同步。離線時做的編輯,會在裝置重新連線時併回來。伺服器上的那份是真相來源。

03分享是什麼意思——一份副本,還是同一個檔案?

分享給的是同一份檔案的存取權,不是一份副本。收到的人在自己的檢視裡看到它。任何更新都會傳到每個有存取權的人。

04資料夾、移動與重新命名算是檔案操作嗎?

不會。資料夾只是 metadata。移動與改名只動 metadata,永遠不碰儲存的區塊,所以不管檔案多大都是瞬間完成。

05分享帶有權限嗎——唯讀 vs 可編輯?

要。每個分享都帶一個角色(viewer/editor),由 metadata 服務強制執行。presigned URL 只限於申請它的那個角色。

06刪除會同步嗎,使用者能救回被刪的檔案嗎?

刪除跟其他編輯一樣同步,用 tombstone。區塊會在垃圾桶窗口裡待一陣子才被回收。一個同步出去的錯誤,需要一條反悔的路。

範圍外原地協同編輯(那是協同文件編輯那一題) · 不下載就預覽與渲染 · 版本歷史的 UI(資料模型保留 latest_version,但瀏覽歷史不在範圍內)

非功能需求

01當網路分裂時,什麼必須繼續運作?

可用性優先於一致性:檔案清單過時個幾秒沒關係;讓上傳失敗則不行。

02一個 50 GB 的上傳在 49 GB 時掛掉會怎樣?

它從最後一個已驗證的區塊續傳——絕不重來。區塊狀態在伺服器端追蹤,所以任何裝置都能接續這次上傳。

03我們如何知道一個檔案在傳輸途中沒有損毀?

每個分塊和整個檔案都帶有 SHA-256 指紋。一個分塊只有在儲存層確認位元組之後,才會被標記為已上傳。

04兩位使用者上傳同一支 2 GB 影片——我們會存兩份嗎?

不會:相同的指紋就代表相同的內容——中繼資料把兩位使用者都指向同一批已儲存的區塊。

05跨裝置同步需要多即時?

幾秒:在線的裝置會被推播變更通知,並以週期性輪詢作為安全網。

持續追問 —— 面試是一場對話

真實面試探得比一份整齊清單深得多。這些範圍問句,區分出真正拷問問題的人和只是背誦的人。

  • 最大檔案大小是多少——幾 MB 還是數十 GB?
  • 原地協同編輯在範圍內嗎,還是只把檔案當成 blob?
  • 當兩個裝置離線編輯同一個檔案,使用者最後應該看到什麼?
  • 我們需要版本歷史,還是只要最新版本?
  • 不同使用者上傳的相同內容應該只存一份嗎?
02

逼出架構決策的數字

把每個估算都當成一種壓力,用來合理化一個元件:快取、佇列、分片、副本、worker pool 或退路。

01

大檔案的區塊數

50 GB 檔案 ÷ 5 MB 區塊50,000 MB ÷ 5 MB = 10,000 個區塊

那就是一萬個並行進行、且各自能獨立續傳的上傳。這正是為什麼一個巨大的 POST 永遠行不通。

02

失敗後的續傳成本

上傳在 98% 時掛掉重試 = 剩下的約 200 個區塊,而不是 10,000 個

區塊狀態追蹤把一次災難性的重來變成 2% 的補齊。

03

去重的效益

同一個檔案被 N 位使用者上傳,指紋相符儲存成本 = 1 份副本 + N 列中繼資料

以內容定址的儲存讓第二次以及之後的每一次上傳幾乎免費。

04

改一個位元組

固定大小分塊 vs 內容定義分塊(CDC)固定:插入 1 位元組會位移每一個邊界 → 幾乎所有區塊都重新上傳 · CDC:只有被動到的區塊

CDC(rolling hash)正是讓被編輯過的檔案做 delta sync 變便宜的關鍵。

05

presigned URL 的時間窗

URL 有效約 5 分鐘,範圍限定在單一區塊外洩窗口 = 幾分鐘 · 波及半徑 = 一個區塊

短效、窄範圍的 URL 就是用戶端直傳的安全論述。

決策範例

數字

一個 50 GB 的檔案是一萬個 5 MB 區塊。在那個尺寸下,有意思的問題不是儲存——而是續傳、去重,以及當兩個裝置編輯同一個檔案時會發生什麼。

我的選擇

用戶端在本機把檔案分塊、算指紋,然後用短命的 presigned URL 把這些分塊並行、直接上傳到 blob storage。API 只處理 metadata,並在標記完成前先驗證每個分塊的 ETag。同步靠推一個通知來運作,接著每個裝置對著一個 changes-since 游標做對帳。遇到衝突時,主副本採 last-write-wins。輸掉的那筆編輯會被留成旁邊的一份衝突副本,所以使用者什麼都不會失去,由他自己去解決。

避免

我不會做的事:把檔案位元組繞經我的 API 伺服器。它們會變成頻寬瓶頸和攻擊面,卻換不到任何好處。我也不會在衝突時無聲丟掉輸掉的那筆編輯。純 last-write-wins 用在檔案「清單」上沒問題。但用在檔案「內容」上,安靜地把某人一整個下午的工作扔掉,就是這樣流失客戶的。

何時改變

如果就地協同編輯進入範圍,區塊層級的同步就是錯的工具。那會變成在文件結構上做 operational transform 或 CRDT。那是另一套設計,也是另一道面試題。

03

架構路徑

先看一張完整的圖,再把每條路徑各自畫成一張圖 —— 寫入路徑與讀取路徑承載不同流量,合理化不同的元件。

完整全貌

總覽 —— 每個元件

Client (SyncAgent)API / MetadataServiceMetadata DBBlob StorageCDNNotificationService變更推播到各裝置Chunk +Fingerprint每個區塊算 SHA-256Chunk Verify(ETags)信任但要查證

檔案位元組直接從 client → blob storage 流動(presigned URL)。只有 metadata 經過 API。通知服務叫其他裝置去拉 changes 游標。

路徑 1

上傳路徑——分塊、算指紋、直傳 blob

ClientChunk +FingerprintPresigned URLsBlob Storage(平行)Metadata:完成

先做去重檢查——已知的指紋不上傳一個位元組就完成。區塊平行上傳;只有在每一個區塊的 ETag 都驗證通過之後,檔案才翻成完成。

路徑 2

同步路徑——先通知,再協調

Device A 儲存Metadata Service推播通知Device B:changescursor經 CDN 下載

推播是門鈴,changes cursor 才是真相:就算漏掉一個通知,下一次輪詢也會自我修復。

04

API 與資料模型

在最佳化之前,先讓契約可被檢視:端點、實體、擁有權、重試與狀態。

POST/files/presigned-url

請求{ name, size, fingerprint, chunk_fingerprints[] }

回應200 { file_id, presigned_urls[] } · 200 { deduplicated: true }(當指紋已存在時)

用戶端用這些 URL 把區塊直接上傳到 blob 儲存——檔案位元組永遠不經過 API 伺服器。

PATCH/files/{file_id}/chunks

請求{ chunk_id, etag }

回應200 chunk 狀態

信任但要查證:伺服器接受用戶端的進度回報,然後在把區塊算作已上傳之前,對 blob 儲存核對 ETag。

GET/files/{file_id}/presigned-url

回應200 { url }(CDN 簽章,短效期)

下載來自 CDN edge;短效的簽章 URL 讓分享連結不會變成永久的公開 URL。

GET/files/changes?since={cursor}

回應200 [{ file_id, change, version }]

同步的骨幹:推播通知「有東西變了」;這個 endpoint 才是裝置用來協調的真相。

核心實體

FileMetadata

file_id (PK) · name · size · fingerprint (SHA-256) · latest_version · status · chunks[] {id, fingerprint, status}

file_id(身分)刻意與 fingerprint(內容)分開——重新命名既不改內容,也不動已儲存的區塊。

SharedFiles

user_id (partition key) · file_id (sort key)

每位使用者對每個分享檔案一列:「這位使用者能看到什麼」是一次單分區查詢。

Device

device_id (PK) · user_id · last_synced_at

同步狀態是逐裝置的——每個裝置從自己的 cursor 之後拉取變更。

05

深入方向

在面試最後三分之一挑一條路線。每條路線給你主題、它該回答的面試官問題,以及要避免的失敗模式。

重點

那個 50 GB 的上傳

把上傳從頭到尾走一遍:分塊、平行、伺服器追蹤什麼,以及它在 98% 時掛掉會怎樣。

回答

把檔案切塊、平行上傳,伺服器端追蹤每塊狀態;在 98% 掛掉就從最後一個驗證過的塊續傳——換任何裝置都行。

避免

一個 multipart POST 加上從零重試——在這個尺寸下,續傳本身就是功能。

重點

同一個檔案,兩個上傳者

兩位使用者上傳一支一模一樣的 2 GB 影片。實際上存了什麼,系統又怎麼知道?

回答

兩份上傳的 SHA-256 指紋相同,區塊只存一份、兩位使用者的 metadata 都指向它;第二次「上傳」其實只是一筆 metadata 寫入。

避免

用檔名或大小去重——只有內容指紋(SHA-256)才定義身分。

重點

兩個裝置離線編輯

筆電和手機都離線編輯了同一個檔案。兩者都上線。使用者最後拿到什麼?

回答

先同步者乾淨勝出;後同步裝置的版本以衝突副本保留在檔案旁——由使用者裁決,系統絕不默默丟棄任何編輯。

避免

默默只保留最後一次寫入——落敗的編輯必須以衝突副本的形式存活下來。

重點

位元組實際上怎麼流動

如果檔案位元組流經 API 伺服器會壞掉什麼,presigned URL 到底保護了什麼?

回答

位元組走 client → blob storage 直傳,用短效、限定單塊的 presigned URL;API 只發 URL 與記 metadata,檔案流量永遠壓不垮它。

避免

長效或涵蓋整個檔案的 presigned URL——有效期只該幾分鐘,範圍只該一個區塊。

重點

大檔案裡的小修改

使用者改了 50 GB 檔案中間的幾個位元組。我們要整個重傳嗎——怎麼只同步變動的部分?

回答

內容定義分塊(content-defined chunking):塊邊界由內容本身決定,小修改只影響碰到的那幾塊,同步就只重傳那幾塊。

避免

對編輯過的檔案用固定大小分塊——插入一個位元組會位移之後所有塊邊界,幾乎每一塊都變新的、都要重傳。

準備好練習了嗎?

把 檔案同步系統 大聲講一遍,讓 AI 為你的說明評分。

用 AI 練習這題 →