實現文件的秒傳、分片上傳以及斷點續傳的功能。使用 Redis 緩存上傳的文件分片信息減輕數據庫讀寫壓力,同時防止有人惡意攻擊服務器導致服務器磁盤爆滿無法提供服務。
🔍 詳解:
1. 實現文件的秒傳、分片上傳以及斷點續傳功能
-
秒傳(Instant Upload):
用戶上傳文件時,先計算文件的 MD5 值,如果該 MD5 對應的文件在服務器上已存在,則不再上傳,直接生成文件引用,極大節省帶寬和上傳時間。 -
分片上傳(Chunk Upload):
將大文件切分為若干小分片(chunks)逐個上傳,提升大文件上傳成功率,避免因一次性傳輸失敗而重傳整個文件。 -
斷點續傳(Resume Upload):
上傳中斷后,下次上傳時從上次已上傳的分片繼續上傳,而不是重新上傳所有數據,提升用戶體驗并節省網絡資源。
2. 使用 Redis 緩存上傳的文件分片信息,減輕數據庫讀寫壓力
- Redis 作為內存型數據庫,用于臨時記錄每個用戶上傳的文件分片狀態(如已上傳的分片編號、大小等)。
- 這樣可以避免頻繁讀寫 MySQL,從而提升系統性能,減少數據庫壓力。
示例:
Key: upload:{userId}:{fileMd5}
Field: chunkIndex1 => uploaded
Field: chunkIndex2 => uploaded
...
3. 防止有人惡意攻擊服務器導致服務器磁盤爆滿無法提供服務
-
攻擊方式:偽裝成正常用戶,不斷上傳分片但不合并,長時間占用磁盤空間。
-
解決策略:
- 使用 Redis + 延時任務(如 ZSet + Kafka)記錄每個上傳任務超時時間;
- 若長時間未完成上傳合并,則觸發清理任務,刪除臨時分片 + 回收空間記錄;
- 通過這種方式防止大量臨時文件殘留占滿磁盤,確保服務可用性。
? 總結:
系統通過實現秒傳、分片上傳和斷點續傳提升上傳效率與用戶體驗,并結合 Redis 緩存與延時任務機制,有效降低數據庫壓力并防御惡意上傳行為,保障磁盤資源和系統穩定性。
文件的秒傳、分片上傳、斷點續傳
🧩 一、文件的秒傳(Instant Upload / Fast Upload)
? 概念
“秒傳”是指:當用戶上傳一個文件時,系統會判斷該文件是否已經存在于服務器上。如果已存在,則無需真正上傳文件內容,直接在數據庫中為該用戶創建一個引用關系,完成上傳操作。
? 實現原理
-
客戶端在上傳前計算該文件的 唯一哈希值(如 MD5 或 SHA256)。
-
將該哈希值發送給服務器。
-
服務器查詢該哈希是否已存在(已上傳的文件表)。
- 存在 → 秒傳成功,僅做數據庫記錄,不傳文件;
- 不存在 → 繼續正常的分片上傳流程。
? 優點
- 避免重復上傳同一文件,節省帶寬;
- 提高上傳速度,尤其是常用/熱門文件。
🧩 二、分片上傳(Chunked Upload)
? 概念
將大文件切成多個小塊(分片),逐個上傳。這種方式適用于不穩定的網絡環境,可以容忍部分上傳失敗而不影響整體進度。
? 實現方式
-
客戶端
- 將文件按固定大小(如1MB)分片;
- 每個分片附帶文件總 MD5、分片序號、總分片數等元信息;
- 分片逐個上傳。
-
服務端
- 接收每個分片,保存至臨時目錄;
- 使用 Redis 緩存當前已上傳的分片索引;
- 等全部分片上傳完畢后,合并成完整文件;
- 寫入正式存儲路徑,并清理 Redis 分片記錄和臨時文件。
? 優點
- 降低失敗重試代價;
- 支持更大的文件上傳;
- 易于并發上傳,提升性能。
🧩 三、斷點續傳(Resume Upload)
? 概念
在上傳中斷(如網絡中斷、瀏覽器關閉)后,用戶再次上傳該文件時,可以從中斷的分片位置繼續上傳,而無需從頭開始。
? 實現機制
- 上傳前,客戶端向服務端查詢該文件對應的已上傳分片列表;
- 服務端從 Redis 或數據庫中查到已上傳的分片序號;
- 客戶端只需上傳未上傳的分片;
- 服務端補齊分片后繼續合并文件。
? 核心點
- Redis 緩存每個文件的上傳狀態(如:哪些分片已完成);
- 分片文件統一保存到臨時目錄,命名規則中含有用戶ID + 文件MD5 + 分片索引;
- 定期清理長期未完成上傳的臨時分片,避免空間浪費。
? 優點
- 提升用戶體驗,上傳中斷可恢復;
- 適用于大文件和移動設備;
- 減少帶寬浪費。
? 總結對比表
功能 | 核心作用 | 關鍵技術點 | 主要優勢 |
---|---|---|---|
秒傳 | 判斷是否重復上傳 | 文件哈希(MD5、SHA256) | 節省帶寬,極速上傳 |
分片上傳 | 大文件分塊分次上傳 | 客戶端分片、服務端合并 | 容錯性強,適配弱網絡環境 |
斷點續傳 | 上傳中斷后斷點恢復 | Redis 緩存、上傳狀態查詢 | 減少失敗重傳,提升用戶體驗 |
🎤 上傳功能模塊詳解
在云盤項目中實現了上傳模塊,詳細說一下你們是怎么處理大文件上傳、斷點續傳、以及秒傳的?
是的,這一塊是我主導設計和實現的,我從三個方面來說:秒傳、分片上傳、斷點續傳,并介紹我們如何利用 Redis 減少數據庫壓力,同時避免惡意攻擊 的問題。
? 第一,秒傳功能
我們在用戶上傳文件前,會先計算整個文件的 MD5 值,然后將這個 MD5 發送到服務器。
- 服務器會先查找該 MD5 是否存在于文件記錄表中。
- 如果存在,說明這個文件已經有人上傳過了,我們就可以直接在數據庫中記錄一條文件引用關系,不需要再次上傳文件。
- 這樣我們就實現了“秒傳”——文件其實沒上傳,但用戶看到已經上傳成功。
這個功能的意義在于可以避免重復上傳文件,節省帶寬和服務器資源,特別是在多個用戶之間共享同一文件的場景下非常高效。
? 第二,分片上傳
對于大文件,我們采用的是 分片上傳 的方式。
- 客戶端會把大文件按一定大小,比如1MB,切成多個分片;
- 每個分片單獨上傳,并攜帶:文件 MD5、分片索引、總分片數等信息;
- 服務端接收后,把分片暫存在臨時目錄中,文件名規則中會包含
用戶ID+MD5+分片索引
; - 并把已上傳的分片信息記錄到 Redis,比如用一個
Set
結構標記當前文件已完成的分片列表; - 等所有分片都上傳完成后,由后端合并這些分片,生成完整文件。
這個機制帶來的好處是:網絡不穩定也能斷點重試,每個分片都可單獨上傳,并行性高,效率也更高。
? 第三,斷點續傳
斷點續傳是基于分片上傳的擴展功能。
- 當用戶中斷上傳后(比如網絡斷了或瀏覽器關閉),再次上傳這個文件時,前端會根據
文件MD5 + 用戶ID
請求服務端查詢已上傳的分片索引; - 服務端從 Redis 里返回已上傳的分片列表;
- 客戶端跳過這些分片,只上傳剩下未完成的分片。
這種方式能顯著提升用戶體驗,尤其是大文件或上傳中斷的場景。
? Redis 在這里的作用
我們沒有直接把每個分片信息寫入數據庫,而是使用 Redis 作為緩存層:
- 一方面,降低數據庫寫入壓力,避免頻繁寫入;
- 另一方面,通過緩存控制上傳行為,防止惡意攻擊造成服務器硬盤爆滿。
比如:
- 有人惡意頻繁上傳大文件的分片但不合并,正常流程不會增加用戶空間;
- 但這些臨時分片文件會持續占用磁盤;
- 我們會通過 Redis + 延時任務機制記錄每個文件的上傳狀態,定期清理未完成的文件;
- 并且維護
use_space_unfinished
這樣一個字段,表示“未合并完成文件的總大小”,和use_space_finished
分開存; - 系統判斷磁盤是否超限,是通過這兩個值之和。
? 總結
這三個上傳功能是密切相關的:
- 秒傳:避免冗余傳輸;
- 分片上傳:支持大文件、容錯性強;
- 斷點續傳:中斷后恢復,提升體驗;
- Redis 緩存:減壓數據庫 + 控制風險;
- 同時我們后續結合了 Kafka 做延時任務調度,對未合并的分片進行智能清理,保證系統可持續運行。
? 面試官可能追問(你可以這樣接)
面試官:如果 Redis 數據丟了怎么辦?你怎么保證數據庫和 Redis 最終一致?
你可以答:
我們每次上傳分片時都會寫 Redis,并通過 Kafka 發送異步消息,延遲執行任務時再將 未完成空間
寫入數據庫,從而保證最終一致性。即使 Redis 崩潰,Kafka 中的消息還能驅動后續數據寫入,避免數據丟失。
如果 Redis 數據丟了怎么辦?你怎么保證數據庫和 Redis 最終一致?
?問題背景
在文件上傳過程中,為了提升性能,我們使用了 Redis 來緩存用戶上傳的文件分片信息(包括已上傳的片段、未完成上傳的總大小 use_space_unfinished
等),而不是直接頻繁更新數據庫。
但問題是:Redis 是內存數據庫,存在數據丟失的風險(如宕機、RDB/AOF未寫入等)。
那么:Redis 掛了或者數據丟了,我們怎么保證數據庫數據依然準確?系統還能恢復嗎?
? 我們的設計目標:最終一致性
我們不是追求強一致性(強一致性下 Redis 就不能丟),而是保證 最終一致性:
即使 Redis 崩潰,通過 Kafka 消息 + 定時任務重處理機制,最終數據庫的數據一定是正確的。
🧠 核心思路:異步延時寫數據庫 + Kafka 兜底
? 1. 上傳過程中,所有用戶的 use_space_unfinished
信息先寫入 Redis
-
每上傳一個分片:
- Redis 記錄該文件當前已上傳的分片列表;
- Redis 增加該用戶的
use_space_unfinished
。
-
每次更新 Redis 的同時,我們會將上傳信息 作為消息發送到 Kafka 延時隊列(或普通隊列配合定時任務處理)。
? 2. Kafka 消費者異步更新 MySQL(延遲寫入)
- Kafka 的消息內容:
{userId, fileMd5, 當前上傳的分片大小, timestamp}
; - Kafka 消費者接收到消息后,經過一定延遲后,再寫入 MySQL 中的
use_space_unfinished
字段; - 此時系統就完成了一次異步更新:Redis 快速響應,Kafka 異步落庫,性能與安全兼顧。
💣 如果 Redis 崩潰了怎么辦?
?? 情況1:短暫宕機,Kafka 尚未消費
- Redis 掛了不影響 Kafka;
- Kafka 中消息仍然存在;
- 消費者重啟后,Kafka 會自動重新投遞未處理消息;
- 仍然可以補償寫入數據庫;
- 數據庫數據不會丟,最終一致性得以保障。
?? 情況2:Redis 掛了、Kafka 也丟了?
-
Kafka 默認有消息持久化機制,消息不會輕易丟;
-
如果真的同時丟(極端情況),我們可以:
- 通過 定時掃描臨時文件目錄,判斷哪些上傳超時未合并;
- 使用用戶上傳記錄、文件碎片路徑等重建上傳記錄;
- 重算
use_space_unfinished
并寫入 MySQL。 - 這屬于“容災補償機制”。
🧰 技術細節總結
技術點 | 作用 |
---|---|
Redis 緩存 | 快速記錄分片狀態與用戶未完成空間占用,避免頻繁 DB IO |
Kafka 異步通道 | 保障數據寫入流程的延遲解耦,實現數據持久化緩沖與補償 |
MySQL 最終寫入 | use_space_unfinished 、分片記錄最終落庫,持久化核心數據 |
延遲任務機制 | 清理超時未完成上傳、維護空間一致性 |
冪等性處理 | Kafka 消費者必須冪等處理(如根據任務 ID 判重),避免重復寫入 |
? 面試中回答:
我們通過 Redis 快速緩存上傳狀態,同時將每個上傳分片的元信息異步發送到 Kafka。Kafka 消費者會延遲一段時間后將上傳占用的未完成空間數據寫入 MySQL,從而實現最終一致性。如果 Redis 崩潰了,我們仍能從 Kafka 補償更新數據庫,確保數據準確。同時我們有超時任務清理機制,能識別上傳失敗或中斷的文件,避免資源泄露。這樣做的好處是既降低了數據庫壓力,又提升了系統的容錯能力和可恢復性。
如何使用 Redis 緩存上傳的文件分片信息,從而減輕數據庫的讀寫壓力,提升性能,并且防止磁盤資源被惡意攻擊耗盡
? 一、問題背景
用戶上傳一個大文件(比如 1GB),我們會將它分成多個小的“分片”進行上傳。例如:
文件A:共10個分片,每個分片10MB
如果每上傳一個分片我們都去操作數據庫:
- 會產生大量頻繁的 寫請求(一次上傳 = N 次寫庫);
- MySQL 屬于磁盤存儲,寫入開銷大;
- 數據庫本身的 QPS 很有限,很容易被打爆。
因此,我們需要一個中間層 —— Redis 緩存。
? 二、Redis 緩存的核心作用
🧠 用 Redis 緩存上傳狀態(代替頻繁寫庫)
在上傳過程中:
- 記錄:當前已上傳的分片編號;
- 臨時計算:該用戶上傳但尚未合并的文件空間(用于判斷是否超限);
- 狀態標記:記錄上傳是否完成、是否需要合并、是否被取消等。
這些數據原本都應寫入數據庫,但 Redis 擁有:
- 高速讀寫性能(QPS > 10萬+);
- 天然支持原子操作、集合管理(比如 Set/Zset);
- 支持過期策略,可定期清理數據;
因此用 Redis 做臨時緩存,可以極大降低數據庫負載。
? 三、Redis 的數據結構設計示意
以下是幾個核心的緩存鍵設計:
Redis Key 名稱 | 類型 | 作用 |
---|---|---|
upload:{userId}:{fileMd5}:chunks | Set | 當前已上傳分片編號集合 |
upload:{userId}:{fileMd5}:size | String | 已上傳分片大小匯總(用于限制未完成空間) |
user:{userId}:use_space_unfinished | String | 用戶未完成上傳所占空間 |
user:{userId}:use_space_total | String | 已完成上傳文件所占空間 |
示例:
SADD upload:123:abcd1234:chunks 1 2 3
INCRBY user:123:use_space_unfinished 10485760 # +10MB
? 四、緩存 + 延遲寫入數據庫(最終一致)
使用 Redis 作為上傳狀態的實時緩存后,我們不立即更新數據庫,而是采用 異步寫入:
- 每次上傳一個分片 → 更新 Redis;
- 同時將信息發到 Kafka 延遲隊列(或加入延遲任務);
- Kafka 消費者延時寫入 MySQL(例如每 10 秒批量寫);
- 上傳完成時合并數據,正式更新數據庫已用空間。
這樣設計的優點是:
- 上傳體驗流暢(Redis 快);
- 數據庫不被頻繁寫入;
- 保障最終一致性,可靠補償;
? 五、上傳完成前如何防止磁盤打爆?
防攻擊策略:
- 每上傳一個分片,就立刻在 Redis 中增加
user:xxx:use_space_unfinished
; - 服務器校驗:
use_space_unfinished + use_space_total ≤ 用戶最大限額
; - 如果超限則直接拒絕上傳請求;
- 定期掃描 Redis 和磁盤上的臨時文件,清理過期未完成的上傳記錄。
? 總結一句話回答:
為了避免每個文件分片都寫一次數據庫,我們使用 Redis 緩存用戶的上傳狀態、分片列表和未完成上傳的總大小。這一方面提升了系統的處理吞吐量,減少了數據庫壓力,另一方面也能實時檢測用戶是否超額占用磁盤資源,防止惡意攻擊。最終一致性則通過 Kafka 異步任務機制或定時任務補償保證。