终端断网一上午,恢复后数据怎么不重不丢:本地队列、断网续传与冲突合并

简介: 从一台终端断网三小时、恢复后补传数据出现重复、丢失和互相覆盖说起,讲清离线优先数据同步的落地:本地 oplog 操作日志结构、clientOpId 幂等键与单调 seq 序号、按序小批量断点续传、服务端去重与缺口检测,以及多端并发修改时的版本号、字段级合并、最后写入获胜(LWW)与冲突标记,附五个踩坑。

有个门店场景的终端装在信号不稳定的位置,运营商线路割接那天上午直接断网三个多小时,但终端不能停,店员照常录入业务记录。等网络恢复,问题来了:补传的数据有的重复进了两遍,有的干脆丢了,还有几条在断网期间被另一台终端改过、合并时互相覆盖。这其实是典型的离线优先(offline-first)数据同步问题,跟具体业务无关,核心是本地队列、幂等补传和冲突合并。这篇把这套机制的落地过程记下来。

一、思路:断网时写本地,恢复后按队列补传

不能等网络可用才让用户操作,而是把终端本地当成可靠的第一写入点:每条记录产生时先落本地存储并标记状态,网络可用时再按顺序补传到服务端。

每条本地记录维护一个操作日志(oplog)结构:

{
  "clientOpId": "c_8a3f_1694_0001",   // 终端生成的全局唯一操作ID,幂等键
  "seq": 2049,                          // 本终端单调递增的操作序号
  "type": "upsert_record",
  "payload": { ... },                  // 具体数据
  "createdAt": 1726450000000,
  "status": "pending"                  // pending / synced / conflict
}

两个标识各有用途:clientOpId 用于服务端幂等去重,同一操作补传多少次都只生效一次;seq 用于本终端操作排序和断点续传,告诉服务端"我已经同步到第几号",漏传的能被发现。这套离线同步机制是在唯顿收银系统的终端上落地的,我主要负责本地 oplog 设计、补传幂等和冲突合并这几块,做完之后断网半天再恢复,数据也能对齐。

二、补传:幂等键 + 顺序 + 断点续传

补传不是把本地数据一股脑重发,而是有明确的去重和续传规则。

服务端以 clientOpId 做幂等:建唯一约束,已存在的 clientOpId 直接返回成功但不重复落库。这样终端因为超时、重试、重启而重复发送同一条操作,服务端都能识别。

// 服务端处理一条补传(伪代码)
async function applyOp(op) {
  const existed = await db.opLog.findOne({ clientOpId: op.clientOpId });
  if (existed) return { duplicated: true, status: existed.status };  // 幂等返回

  // 校验 seq 连续性,发现缺口说明有操作漏传
  const lastSeq = await db.opLog.maxSeq(op.deviceId);
  if (op.seq > lastSeq + 1) {
    return { needGap: true, fromSeq: lastSeq + 1 };   // 要求终端补传缺口段
  }
  await db.opLog.insert({ ...op });
  await applyToBusiness(op.payload, op.version);
  return { duplicated: false, status: "synced" };
}

终端侧补传按 seq 升序、小批量(比如每批 50 条)顺序提交,一批全部成功才把本地状态改成 synced;中途失败从最后一个未确认的 seq 续传,而不是从头再来。本地存储要选落盘可靠的方案(终端上用 SQLite 或带持久化的本地库),避免应用被杀掉后 pending 记录丢失。

三、冲突合并:版本号 + 最后写入获胜 + 冲突标记

多台终端或终端与后台同时改同一条记录时,需要一套确定性的冲突规则。我用的是"版本号 + 最后写入获胜(LWW)+ 冲突兜底":

服务端更新逻辑:
1. 每条记录带 version(整数)和 updatedAt;
2. 终端更新时携带它读到的 baseVersion;
3. 若 baseVersion === 服务端当前 version,直接更新,version + 1;
4. 若 baseVersion < 服务端 version,说明期间被别人改过:
   - 字段级可合并的(不同字段),自动合并;
   - 同一字段都被改,按 updatedAt 较新者胜出(LWW);
   - 无法安全合并的,置为 conflict 状态并保留两份,交后台规则或人工确认。
// 字段级合并示意
function merge(serverDoc, clientDoc, baseVersion) {
  if (clientDoc.baseVersion === serverDoc.version) return { ...clientDoc, version: serverDoc.version + 1 };
  const merged = { ...serverDoc };
  for (const key of Object.keys(clientDoc.changes)) {
    const sTime = serverDoc.fieldUpdatedAt[key] || 0;
    const cTime = clientDoc.fieldUpdatedAt[key] || 0;
    if (cTime >= sTime) merged[key] = clientDoc.changes[key];   // 较新者胜
  }
  merged.version = serverDoc.version + 1;
  return merged;
}

纯 LWW 会静默丢数据,所以对"同一关键字段被双方修改"这种情况,我没有让它无声覆盖,而是标记 conflict 并保留双方内容,宁可让人确认一次,也不丢一条记录。

四、我踩过的五个坑

  1. 补传没有幂等键:超时重试导致同一条记录落两遍,加 clientOpId 唯一约束后彻底解决;
  2. 只靠 updatedAt 去重:终端时钟不准会错乱,幂等交给服务端生成约束、时钟只用于 LWW 比较;
  3. 一次性全量重发:断网半天积压上千条,整包重发又慢又容易超时,改成按 seq 小批量顺序补传;
  4. 冲突一律后写覆盖:同字段被改时静默丢了较早的修改,加版本号判断和 conflict 标记后才安全;
  5. pending 状态只放内存:终端重启后待补传记录丢失,oplog 必须落盘并在启动时扫描恢复。

五、几个容易忽略的细节

  • 同步状态机要显式区分 pending、synced、conflict,UI 上让用户看得到"还有多少条没同步";
  • 补传要处理终端时钟回拨和跨天积压,排序一律以服务端确认结果为准;
  • 删除操作也要进 oplog(软删除标记),否则断网期间删的记录恢复后又冒出来;
  • 建议在服务端按 deviceId 记录已同步水位,方便排查"终端说发了、服务端没收到"的纠纷。

复盘要点

  • 离线优先的关键是"本地可靠落盘 + 全局幂等键 + 单调序号 + 小批量顺序补传",让重复发送无害、漏传可发现;
  • 冲突不要无脑后写覆盖,版本号做并发检测、字段级合并加 LWW、不安全就标记保留;
  • oplog 覆盖增删改全部操作并持久化,是断网恢复后数据不重不丢的底座。

以上是个人实践记录,各平台具体功能以官方实时信息为准。

你们做弱网或断网场景的终端数据同步时,冲突是怎么处理的?是直接最后写入获胜,还是做了字段级合并?

相关文章
|
7天前
|
人工智能 API 内存技术
刚刚 DeepSeek V4.1 Flash 开启内测,1 分钟教你用上!
刚刚 DeepSeek 内测群发布了 DeepSeek V4.1 Flash 中间版本内测的消息,这次的模型采用了新的结构,原生支持多模态、能力更强、速度更快、且成本更低。
1799 10
|
12天前
|
人工智能 运维 BI
阿里云千问办公QwenWork深度解析:基于Qwen3.8,六大核心能力重构企业全自动化工作流与计费选型指南
传统AI办公工具大多停留在对话问答、文档摘要、简单文案生成层面,只能完成单点碎片化任务,无法自主拆解复杂业务流程,很难串联多工具、多文档、外部业务系统完成端到端完整工作交付。很多企业在落地AI办公的时候,需要组合多款不同工具,来回切换界面,手动复制粘贴中间结果,智能化改造落地门槛居高不下。千问办公QwenWork是整合多款智能体产品能力打造的一体化企业办公智能体平台,底层基座依托Qwen3.8大模型,打通桌面端Agent、云端Agent、企业协同Agent三种运行形态,不再局限简单问答,接收业务目标之后自主拆解任务步骤,调用各类工具,处理文档、表格、浏览器自动化、数据查询,直接输出可交付的办公
1644 3
|
13天前
|
网络协议 Linux iOS开发
【2026实测】Wireshark下载+安装+汉化+使用教程(图文版,巨详细)
Wireshark 是一款免费开源的网络协议分析工具,可实时捕获、解析并可视化数据包,助你诊断网络故障、分析通信协议(如HTTP、DNS、TCP等)。支持Windows/macOS/Linux,含中文界面,新手入门便捷。(239字)
|
8天前
|
SQL 人工智能 前端开发
QoderWake 1.0 正式发布:从桌面里的 Agent,到工作现场的数字员工
QoderWake v1.0正式发布:企业级数字员工团队平台。支持“一句话建岗”,预置10类特训岗位;Waker常驻钉钉/飞书群,@即响应、自动协作、跨任务记忆;具备定时/事件/API多触发方式与统一任务看板;已沉淀27.6万条记忆、12.3万项技能,助力组织实现人机协同增效。
783 2
|
6天前
|
缓存 测试技术 API
DeepSeek V4.1 Flash 内测接入:改个模型名即可调用(附代码)
DeepSeek V4.1 Flash 内测不用申请,base_url 不变、改个模型名就能调,9/10 到期。本文讲清接入、计费限流与多模态注意点。
809 0
DeepSeek V4.1 Flash 内测接入:改个模型名即可调用(附代码)
|
20天前
|
人工智能 自然语言处理 安全
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
本文聚焦阿里云2026年推出的三款自研AI办公产品,清晰拆解千问办公、Qoder Teams、Qoder CN的差异化定位与能力边界:千问办公主打职场全场景提效,支持自然语言指令一键完成PPT生成、数据分析等高频办公任务;Qoder Teams面向程序员团队,深度整合AI代码生成、团队协同与企业知识库能力;Qoder CN则专为金融、政务等强合规场景打造,实现数据不出境与VPC私有化部署。文章同步给出分场景选型指南与最新活动定价,帮助不同类型的企业按需组合产品,实现业务岗、研发岗与强合规场景的AI能力全覆盖。
3976 5
阿里云千问办公、Qoder Teams、Qoder CN区别与选择指南:模型能力、适用场景与最新活动参考
|
11天前
|
人工智能 自然语言处理 安全
阿里云AI数智鉴密:AI 生成内容如何拿到一张"防篡改的身份证"
隐形水印 + C2PA签名:让AI生成内容“持证上岗”。
1156 0
|
13天前
|
缓存 数据可视化 开发工具
DeepSeek Harness 怎么更新?dsh 更新完整指南:更新本体(npx、npm、源码)与更新插件两种方式
DeepSeek Harness 的更新分两层:本体更新(npx 自动最新、npm update -g、源码 git pull)与插件更新(插件市场点更新、命令行覆盖安装)。本文按「准备 → 更新本体 → 更新插件 → 更新后检查」四步走,覆盖新手常见疑问。
1554 1
DeepSeek Harness 怎么更新?dsh 更新完整指南:更新本体(npx、npm、源码)与更新插件两种方式
|
6天前
|
人工智能
千问办公官网入口:阿里AI办公QwenWork产品页和免费网页端链接
千问办公官网含两大入口:一是网页端(qwenwork.cn),即开即用,支持浏览器直接访问;二是阿里云产品页 https://t.aliyun.com/U/JNKJuO 提供免费/付费版详情、功能介绍及使用指南。

热门文章

最新文章