文档异步入库、分块上传与断点续传

快速回顾

项目 内容
核心目的 上传与解析、分块、向量化解耦,支持大文件续传和任务恢复
处理链路 MinIO 分片上传 → 合并确认 → MySQL 状态与 Outbox → Kafka → 后台处理
状态与恢复 Redis Bitmap 记录进度;MinIO 校对实际分片;MySQL 保存阶段 checkpoint
一致性 稳定业务键、条件更新、稳定 chunk ID upsert、Outbox 重试发布

面试主回答

文档解析、分块和向量化耗时较长,如果放在上传请求里同步完成,用户等待时间会很长,处理失败也会影响上传接口。因此我用 Kafka 解耦文件上传和后台入库,上传完成后投递文档任务,由消费者执行后续处理。Kafka 的日志和消费位点便于追踪、恢复处理进度,消费者组也便于按负载扩展。

它并非所有场景下都优于其他 MQ;RabbitMQ 的任务队列和路由语义更直接,选型还要看任务规模、团队经验和现有基础设施。无论使用哪种 MQ,都要处理重复消费、失败重试和任务状态,不能把消息投递成功等同于文档已经可检索。

用户创建上传会话后,后端校验文件信息和访问权限,再向 MinIO 创建 Multipart Upload 并取得 uploadId。客户端按序号上传各分片,记录每片返回的 ETag;Redis Bitmap 记录分片进度,供续传时快速查询。网络中断后,客户端使用原会话补传未完成分片。

恢复时与 MinIO 的实际分片列表核对,避免 Redis 进度丢失或过期造成状态不一致。全部分片完成后,后端按序号和 ETag 请求 MinIO 合并对象,确认原文件可用后再更新任务状态并投递 Kafka 入库任务。上传完成和入库完成是两个不同状态:只有解析、分块和索引流程成功后,文档才可检索。

原理与选型

为什么选择 Kafka,而不是其他 MQ?

面试回答

我们这条链路里,文件上传之后还要解析、分块和向量化,处理耗时比较长,所以需要把上传接口和后台任务解耦。我选择 Kafka,主要看中它的分区日志、消息保留和消费位点,任务失败后可以重新消费消息,消费者组也便于扩展处理能力。

上传完成后,系统发布文档入库任务,消费者根据数据库里的阶段状态继续处理。Kafka 负责消息传递和重放,具体恢复到哪一个阶段仍由业务 checkpoint 决定。RabbitMQ 也能完成这种异步任务,它的队列和路由语义更直接;我们这套方案更侧重消息重放和消费进度管理,因此使用 Kafka,并在业务侧补齐幂等和失败恢复。

实现细节

Kafka 消费位点描述消息消费进度,业务 checkpoint 描述文档处理进度,两者需要配合。更换 MQ 也不能省掉任务状态与幂等控制。消息发布成功后,文档仍需完成索引才能进入可检索状态。

比较点 Kafka RabbitMQ
主要机制 分区日志、消费位点、消费者组 任务队列、消费确认与消息路由
本项目的考虑 便于任务追踪、消息重放和消费者扩展 也能承担异步任务,队列与路由语义更直接
共同需要处理 重复消费、失败恢复、业务任务状态 重复消费、失败恢复、业务任务状态
选择依据 任务规模、已有基础设施与团队经验 任务规模、已有基础设施与团队经验

实现与恢复

MinIO 分片上传与断点续传的流程是什么?

面试回答

整体分成创建会话、上传分片、合并确认和触发入库几个阶段。用户发起上传后,后端先校验文件和权限,再向 MinIO 创建 Multipart Upload,拿到 uploadId。客户端按分片序号上传,保存每片的 ETag,Redis Bitmap 用来快速记录哪些分片已经完成。

网络中断后沿用原来的上传会话,先与 MinIO 的实际分片列表核对,再补传缺失部分,避免只相信缓存进度。全部分片上传成功后,后端按序号和 ETag 请求合并,并确认目标对象可用。之后在数据库中更新文档状态、写入 Outbox 事件,再由发布器投递 Kafka。后台完成解析、分块和索引后,文档才会标记为可检索。

实现细节

uploadId 标识 MinIO 的一次分片上传,partNumber 决定分片次序,ETag 用于合并时指定已上传的分片。Redis 进度可能过期或丢失,恢复以 MinIO 中实际存在的分片为校对依据。上传状态与索引处理状态分别维护。

步骤 关键动作 记录或检查
1. 创建会话 校验文件与权限,创建 Multipart Upload uploadId 与上传会话
2. 上传分片 按 partNumber 上传,保存返回 ETag 分片序号、ETag、Redis Bitmap
3. 中断续传 使用原会话,校对 MinIO 实际分片 补传缺失分片,纠正缓存进度
4. 合并文件 按序号与 ETag 提交合并 确认目标对象实际可用
5. 触发入库 更新文档状态并写入 Outbox 事件 由发布器投递 Kafka
6. 完成索引 解析、分块、向量化与索引完成 推进为可检索状态

Kafka 重复消费如何保证幂等?

面试回答

我按消息可能重复到达来设计消费逻辑,先用文档版本和处理阶段组成稳定业务键。消费者拿到消息后,先检查数据库里的阶段状态,已经完成的直接跳过,处理到一半的根据持久化 checkpoint 从未完成的位置继续。

对于最终的索引写入,我给每个子块生成稳定的 chunk ID,使用 upsert,重复处理时更新同一条记录,避免新增重复索引。每个阶段都在产物和状态落库后再提交消费进度。这样即使处理成功后提交位点失败,消息再次到达,也可以根据已有状态安全跳过。Redis 可以辅助加速判断,但持久化状态才是恢复依据。

实现细节

阶段业务键使用 documentVersionId + stage,不能只用文档名称或本次消息 ID。阶段完成判断与持久化写入需要配合,避免并发处理时重复推进状态。存在非幂等的外部副作用时,另存调用状态,并通过结果核对或补偿处理。

层次 关键标识或机制 重复到达时
任务阶段 documentVersionId + stage 查询持久化状态,跳过已完成阶段
阶段产物 持久化 checkpoint 与产物 从未完成位置继续
索引写入 稳定 chunk ID + upsert 更新同一条索引记录
外部副作用 调用状态记录与补偿 先确认结果,避免重复执行

消费失败如何重试和恢复?

面试回答

消费失败后,我先判断错误能否通过重试恢复。模型超时、存储暂时不可用这类临时故障采用退避重试;文件格式错误、内容为空这类确定性问题,继续重试没有意义,就记录原因,停止自动重试并转人工或死信流程。

恢复依赖数据库里的阶段 checkpoint。解析、分块和向量化各阶段完成后,先保存产物和状态,再提交消费进度。Worker 重启或者消息重放时,先读取已完成阶段,从失败位置继续,避免每次重新处理整个文件。重复到达的消息仍然走同一套幂等逻辑,所以重试和恢复可以配合使用。

实现细节

恢复前需要同时确认阶段状态与对应产物,不能仅凭一个“成功”标记跳过缺失结果。消费进度确认与业务阶段完成分开理解,阶段中断不能提前当作处理成功。

情况 判断或状态 处理方式
模型超时、存储暂不可用 临时故障 退避重试
格式错误、内容为空 确定性错误 停止自动重试,记录原因并转人工或死信流程
Worker 重启或消息重放 已保存阶段 checkpoint 跳过已完成阶段,从未完成阶段继续
阶段执行成功 产物与状态均已持久化 再提交消费进度

上传完成但 Kafka 消息投递失败如何补偿?

面试回答

这里涉及数据库和 Kafka 两个系统,我使用 Transactional Outbox 保证任务不会因为发送失败而丢失。MinIO 文件合并确认成功后,在同一个数据库事务里更新文档状态,同时写入一条待发布事件,事务提交后再由独立发布器发送 Kafka。

Kafka 暂时不可用时,事件仍然保留在数据库,发布器后续继续重试;收到 Kafka 确认后才标记已发布。如果发送成功但标记前进程崩溃,事件可能再次投递,这部分交给消费端幂等处理。这样通过持久化待发布事件解决漏投递,通过幂等解决可能的重复投递。

实现细节

Outbox 事务覆盖文档状态与事件记录,MinIO 对象合并发生在这一事务之前。发布器不能在得到 Kafka 确认前将事件标成已发布;发布结果不确定的事件保留重试资格。

环节 持久化或确认 失败后的行为
数据库事务 同时更新文档状态与写入待发布事件 事务失败则一起回滚
发布器投递 扫描 Outbox 并发送 Kafka 未获确认的事件保留并重试
确认发布 收到 Kafka 确认后标记已发布 标记前崩溃可能重复投递,由消费端幂等处理

分片合并失败如何处理?

面试回答

我先区分分片不完整、合并明确失败和合并结果不确定几种情况。合并前校验分片序号和 ETag,并和 MinIO 的分片列表核对;如果缺片,就保留上传会话,让客户端补传后再合并。

如果调用合并接口超时,不能直接认为文件没有生成。我会先查询目标对象,核对文件是否已经完整可用;已经成功就推进后续入库,确实未成功且原上传会话仍有效时再重试。会话已经失效就重新创建上传。只有确认原文件可用后才触发入库,失败过程中保留状态和原因,便于续传或清理遗留分片。

实现细节

合并成功通常会结束对应 Multipart Upload,因此超时后再次找不到上传会话,不能单独据此判断失败,需要结合目标对象检查。长期未完成的会话和分片应清理,避免存储长期占用;清理前检查任务仍是否有效。

故障位置 判断依据 恢复方式
分片不完整 MinIO 实际分片与预期列表 保留会话,补传缺失分片
分片信息不一致 partNumber 与对应 ETag 校正后重新请求合并
合并请求超时 目标对象是否已经生成 先确认结果,再决定是否重试
上传会话过期未完成 会话有效期与处理状态 按清理策略回收
合并明确成功 对象已确认可用 推进数据库状态并触发入库

易混点与回答检查

易混点:上传成功、消息发布成功和文档可检索是不同状态;Redis Bitmap 用于进度加速,恢复仍要核对 MinIO。

回答检查

补充记录

简历表述:基于 Kafka 将解析、分块与向量化转为后台任务;结合 Redis Bitmap 与 MinIO 分片上传实现大文件断点续传。

事实 / 代码依据:待补充。

复盘手记

搜索笔记

输入关键词,搜索所有主题与问答。