文件的秒传、分片上传以及断点续传 || Redis缓存减轻数据库读写压力
实现文件的秒传、分片上传以及断点续传的功能。使用 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 异步任务机制或定时任务补偿保证。