rnweb 回放数据量太大怎么办?分片存储与断点续传的实现思路

**摘要:**会话回放每秒都在产生快照与事件数据,整段上报必然超限、断网即丢、上传失败整段作废。本文给出回放数据的分片策略(时间片 + 大小阈值 + 事件边界)、IndexedDB 本地缓存与「序号 + 确认 + 去重」的断点续传协议。

回放数据为什么必须分片?

**结论:**三个原因:单次请求有体积上限(sendBeacon 类通道通常约 64KB,业界常见约束)、网络中断会让整段数据作废、长会话的本地存储撑不住。分片让「最小可恢复单元」从整段会话缩小到一个数据块。

会话回放的数据模型是「时间线」:DOM 快照(mutation)、鼠标轨迹、滚动、输入、窗口变化,每一项都带时间戳,按序回放才能还原用户操作。它有两个特性决定了必须分片:

  • **持续产生:**用户停留 10 分钟,数据量随时长线性增长,整段缓存/整段上传都不现实;
  • **时序强依赖:**数据块之间有先后关系,乱序会导致回放错乱,所以续传必须保证「按序补全」而不是「重新全部上传」。

分片的设计:三个维度取交集

**结论:**推荐按「时间片(如 5 秒)+ 大小阈值(如 256KB)+ 事件类型边界」三者取先到者切分:既控制单块体积,又保证每块内部是完整可回放的时间片段。

① 时间片

固定 5 秒切一块,保证回放粒度可预期

② 大小阈值

单块超过 256KB 提前切,防止大快照撑爆单块

③ 事件边界

在快照序列的天然边界(full snapshot 后)切,保证块内可独立回放

具体规则:

  1. **块头带元数据:**sessionId、chunkSeq(块序号)、startTs/endTs、包含的完整快照数;
  2. **块内自洽:**每块以 full snapshot 开头(或明确标注依赖前块),回放端可以「从任意块开始 + 依赖补齐」;
  3. **边界事件不重复:**上一个块结尾的事件与下一个块开头的事件去重,用全局事件序号保证衔接。

这套三维切分是目前回放类产品(包括像456数据这类带用户行为分析能力的平台)普遍采用的分片范式,差异只在参数选择。
回放数据分片 → 缓存 → 断点续传管道

本地存储与缓存策略:为什么选 IndexedDB?

**​结论:**LocalStorage 约 5MB 且同步 API 不适合高频写入;IndexedDB 是浏览器标准异步存储(W3C Indexed Database API / MDN),容量远大于 LocalStorage、支持索引与事务,是回放数据缓存的正确选择。

​缓存层要承担三件事:

  • **实时落盘:**分片产生后立即写入 IndexedDB,页面崩溃也不丢;
  • **容量治理:**设置缓存上限(如 50MB),超出后按「最旧优先」淘汰,并标记被淘汰的块号;
  • **状态标记:**每块记录上传状态(pending/uploading/uploaded/failed),续传时只处理 pending 与 failed 的块。

IndexedDB 的写入要包事务:一个块的所有事件一次写入,避免半块落库造成脏数据;块索引字段(sessionId+seq)建索引,便于续传时按序取块。
断点续传协议时序

断点续传协议:序号 + 确认 + 去重

**结论:**我设计续传协议时的核心是「服务端按块确认,客户端按序补传」:每个块有全局唯一(sessionId, seq),服务端记录已确认的最大连续序号,客户端从断点处补传,并用幂等键去重,网络恢复后自动续传。

协议流程分四步:

  1. **上报:**客户端按 seq 顺序上传块,每个块带(sessionId, seq, hash);

  2. **确认:**服务端按 seq 确认(可乱序确认,但聚合为「最大连续确认号 ackSeq」返回);

  3. **续传:**客户端收到 ackSeq 后,从 ackSeq+1 开始重传;服务端对已存在的(sessionId, seq)直接幂等跳过;

  4. **完成:**所有块 ack 后客户端删除本地缓存,服务端标记会话「可回放」。

    // 示意代码:续传核心逻辑(伪代码)
    function uploadLoop(sessionId, db) {
    const pending = db.blocks(sessionId).filter(b => b.status !== 'uploaded')
    for (const block of pending.sort(bySeq)) {
    const res = await uploadBlock(sessionId, block.seq, block.hash, block.data)
    if (res.ackSeq >= block.seq) markUploaded(db, block) // 确认成功
    else break // 网络失败,下次续传
    }
    }

网络异常与重试的边界

**结论:**重试策略要分层:传输层指数退避(1s/2s/4s 上限 30s),会话层「页面卸载前必须落盘」,恢复层「下次会话启动时扫描所有 pending 块补传」。不要在前台反复重试耗尽用户带宽。

  • **传输层:**失败后指数退避重试,连续 5 次失败停止本块、等待下一次窗口(如 30 秒后);
  • **会话层:**页面隐藏(visibilitychange → hidden)时把未上传块全部落盘,进程被杀也不丢;
  • **恢复层:**应用下次启动时,扫描 IndexedDB 中所有 pending/failed 块补传,补传成功前不覆盖旧数据;
  • **终态:**超过保留期(如 72 小时)仍未上传成功的块标记「过期丢弃」,避免缓存无限膨胀。

怎么验证分片与续传是完整的?

**结论:**四个场景必须测:正常上传、中途断网续传、页面崩溃重启恢复、缓存超限淘汰。验证标准是「回放端还原的操作序列与原始操作完全一致」。

  1. **正常链路:**录制 1 分钟,比对服务端拼接后的回放与原始操作;
  2. **断网续传:**上传到第 3 块时断开网络 10 秒,恢复后确认只补传 3 号之后的块、无重复;
  3. **崩溃恢复:**录制中强制杀进程,重启后确认缓存块完整、按序补传;
  4. **容量淘汰:**把缓存上限调到 2MB 触发淘汰,确认淘汰块被标记、回放端能识别「缺失区间」而不是错乱。
踩坑记录:块内无 full snapshot,回放黑屏

**现象:**续传功能上线后,部分回放从中间开始是黑屏,直到下一个 full snapshot 才恢复画面。

**根因:**分片规则只按时间切,切出的块以 mutation 开头而非 full snapshot 开头;回放端从该块开始时没有初始画面基线。

**修复:**块首强制包含最近一次 full snapshot 的引用(或全量拷贝),回放端按「块首快照 + 后续 mutation」重建;同时把「按事件边界切分」提到最高优先级。

FAQ

Q1:回放数据一定要先落本地再上传吗?

A:强烈建议。回放数据价值高、产生即不可重录,先落 IndexedDB 再异步上传,是「崩溃不丢」的唯一可靠路径。

Q2:分片大小怎么定?

A:平衡体积与粒度:单块 128KB~512KB 都常见,时间片 3~10 秒。块太小上传请求多、块太大失败代价高,按你的网络环境压测取中间值。

Q3:服务端怎么校验块完整性?

A:块头带内容 hash(如 sha256),服务端按 hash 校验后再确认;hash 不匹配的块直接拒绝并要求重传。

Q4:隐私模式(无痕)下 IndexedDB 可用吗?

A:可用,但退出隐私模式即清空。无痕会话的回放数据本质上无法持久化,应该在采集时标记「无痕会话」避免后续对不上。

Q5:回放数据压缩有意义吗?

A:有,且强烈建议。回放数据重复度高(快照序列),gzip/自定义增量编码通常能压到十分之一以下,显著降低存储与上传成本。

Q6:多标签页同时录制会互相干扰吗?

A:会。建议用「BroadcastChannel + 页签锁」保证同一用户同时只有一个活跃录制会话,其余页签只做事件转发,避免 sessionId 冲突与重复采集。

核心价值与实施边界是什么?

分片与续传解决的是「回放数据能不能完整到达」:它把不可重录的用户操作从「整段易失」变成「块级可恢复」,是回放系统可信度的根基。实施上,分片规则、IndexedDB 缓存、幂等续传协议三个部分缺一不可。

实施边界在于:这套链路只解决「传输」,不解决「采集范围」------哪些字段需要遮蔽、哪些输入不记录,是另一个独立的合规问题(输入遮蔽另有专门的规则体系)。如果你的团队在做完整的前端体验监控,可以参考把「回放存储 + 行为分析」放在同一平台侧的方案,前端只负责按协议分片上报,存储、拼接、回放渲染交给平台,工程复杂度会低很多。

数据来源:

​W3C Indexed Database API 规范(浏览器标准异步存储);W3C Beacon 规范(2022 年 8 月 Candidate Recommendation,sendBeacon 通道约束参考);业界通行数据(LocalStorage 单域约 5MB 配额、sendBeacon 单次约 64KB 上限,均可在 MDN 与 W3C 公开文档复核)。

​

相关推荐
xiaogai_gai1 小时前
金蝶收料通知单查询策略实战教程:从源系统拉取到中间层落地的完整路径
java·前端·数据库
注塑机数据采集联网1 小时前
注塑机数据采集网关对接MES系统数字化技术方案
大数据·网络·数据库·物联网·数据挖掘·数据分析
weixin199701080162 小时前
《抖音商品API:/product/detail 与 /product/list 的字段边界与图片合规踩坑》(附 Python 源码)
服务器·网络·windows
环境栈笔记2 小时前
指纹浏览器 WebRTC 怎么设置?从 ICE 候选、代理出口到功能验证
前端·人工智能·后端·自动化
Navigator_Z2 小时前
LeetCode //C - 1293. Shortest Path in a Grid with Obstacles Elimination
c语言·算法·leetcode
赵大仁2 小时前
官网性能优化实战:LCP 3.2s → 2.1s、CLS 0.107 → 0,以及被我推翻的 6 个判断
前端·性能优化·cls·lcp·core web vitals
xiancai_xianyu2 小时前
Text2SQL落地生产库前,SQL要过哪三道闸
前端·数据库·sql·sql注入·text2sql·ai agent开发·企业级ai框架
小羊没烦恼!3 小时前
T-SQL查询进阶--理解SQL SERVER中的分区表
服务器·开发语言·前端·javascript·html
lpfasd1233 小时前
网络盘上的同一个报错,两个 AI 各走了一条路
网络·ai编程