鸿蒙端云一体待办清单:30 秒全量轮询的取舍与增量同步改造分析

举报
yd_239758213 发表于 2026/11/12 11:26:47 2026/11/12
【摘要】 以鸿蒙开发实践中心「云同步待办清单」案例的 30 秒全量轮询为起点,分析它在教学阶段的合理性与性能代价,给出增量同步的最小改造方案(毫秒级 updated_at + 软删除墓碑 + 服务端 sync_token 游标),并指出三条最容易丢数据的边界。

背景与问题

今天在鸿蒙开发实践中心读了「云同步待办清单:鸿蒙端云一体实战」案例。它的同步方案是:端侧 ArkTS 应用每 30 秒调用一次 GET /todo/pull,把用户全部任务全量拉回本地重建列表;云侧是 FunctionGraph 的 8 个云函数 + APIG 专享版 + RDS MySQL 三张表(users / sessions / tasks),客户端用 UUID 做任务主键,写入冲突时用 ON DUPLICATE KEY UPDATE 兜底。

这个方案在教学场景下跑得通,也完整覆盖了注册、登录、增删改、搜索和多端同步。但我读完后的第一个问题是:全量拉取能撑多久?任务数从几十条涨到几百条、再多一个设备同时在线时,30 秒一次的全量传输、每次全表 SELECT、每次客户端全量 diff,是不是一种“前期省事、后期还债”的选择?本文就围绕这个取舍展开:先承认全量轮询的合理性,再给出增量改造的最小方案和三条必须守住的边界。

关键分析

先说结论:全量轮询不是偷懒,是教学案例里有意为之的取舍

读案例和仓库代码能看到,全量拉取有三处明确的好处。

  1. 天然幂等、自愈能力强。客户端任何一次数据丢失、错乱,下一次 pull 就能自动修复;没有游标、没有状态机,出问题时排查路径只有一条:拉一次全量。案例的“高频问题排查”里把“冷启动 504 自动重试 3 次”写得很细,正是这种“拉一次就有”的思路。
  2. 服务端可以保持无状态。函数里不需要维护游标、版本号或变更日志,tasks 表只需要业务字段。
  3. 和“客户端 UUID 主键 + ON DUPLICATE KEY UPDATE”的写入策略自洽。多端新建任务不会因为自增 id 冲突;全量拉取再按 id 落库,重复没关系。

代价也很直接。当任务总量到 500+、且多个设备同时轮询时,每 30 秒重复传输全部任务、每次 SELECT * 全表、每次客户端全量 diff,带宽和耗电都线性增长,且与用户实际变更量无关。对教学案例这不是问题,对长期使用的产品就是。

维度 全量拉取(现状) 增量拉取(改造后)
带宽 O(全部任务)/30s O(变更任务)/30s
客户端处理 每次全量 diff 直接 upsert 少量行
数据库查询 SELECT * 全表 WHERE updated_at > ? 走索引
可扩展性 任务 500+ 后明显变慢 只与变更量相关,与总量无关

增量改造要付出的三个代价

  1. 不再自愈。全量是天然幂等的;增量一旦游标丢失或错位(比如 since 值被覆盖、客户端清缓存后用错时间点),就可能持续漏拉,且不容易发现。改造后必须给客户端保留一条“全量兜底”路径(比如登录时或每周一次主动拉全量)。
  2. 写入路径都要维护 updated_at。创建、更新、删除 task 的每个地方都必须正确更新时间戳,漏一个字段,那条数据就永远收不到增量。案例里 8 个函数共享了 parseBody() 这样的公共逻辑,更新时间戳可以放在同一个写入出口集中处理,降低漏改概率。
  3. 删除变复杂。硬删除后增量查询查不到那行,客户端永远不知道要删除它。这是我认为最大的改动点,必须把删除改成软删除(见下)。

方案或实现

改造的最小切面是三处:表结构、拉取接口、客户端游标。

1. 表结构:一列时间戳 + 一个墓碑字段

ALTER TABLE tasks
  ADD COLUMN updated_at TIMESTAMP(3) NOT NULL
    DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  ADD COLUMN deleted_at TIMESTAMP(3) NULL,
  ADD INDEX idx_updated_at (updated_at);

为什么用 TIMESTAMP(3) 而不用秒精度的 DATETIME:MySQL 的 DATETIME 默认精度是秒,同一秒内的两次更新会被 updated_at > since 漏掉。毫秒精度把碰撞概率压得很低(仍非零,所以下面还要处理)。

删除改为软删除:

UPDATE tasks SET deleted_at = NOW(3), updated_at = NOW(3) WHERE id = ?;

客户端收到 deleted_at IS NOT NULL 的行,就在本地删除该任务。墓碑记录可以定期清理(比如 30 天后物理删除),但同步窗口内必须保留——否则客户端在窗口期外又收不到删除信号。

2. 拉取接口:只加一个 since 参数,不动原有结构

GET /todo/pull
GET /todo/pull?since=2026-11-12T03:20:00.123Z

服务端:

SELECT * FROM tasks
WHERE user_id = ? AND updated_at >= ?
ORDER BY updated_at, id;

注意这里我用 >= 而不是 >:同一毫秒内两次更新仍可能碰撞,>= 会让客户端对边界行做重复接收(幂等落库去重,成本是一次多余 upsert),而 > 一旦漏掉就再也追不回来。在“宁可重复、不可漏掉”的取舍里,我选 >=。

另外,案例原有的设计(parseBody 解码 APIG 的 base64 body、dateStrings + getBeijingTime() 消除 8 小时时差)在改造里一个都不用动——时间戳比较完全发生在服务端,客户端不参与时间计算。

3. 客户端游标:只信服务端返回的 sync_token

这是最容易踩的坑:since 绝不能取自客户端本地时钟。设备时钟可能不准、可能被用户改过、时区和服务端也可能不同。

正确流程是:

  • 第一次:GET /todo/pull,服务端返回全部任务 + sync_token = max(updated_at);
  • 后续:GET /todo/pull?since=<上一次返回的 sync_token>;
  • 如果本次结果为空,服务端返回当前 NOW(3) 作为游标推进,避免因为“没有变更”导致客户端的 since 永远停在原地。

同时要强调:拉取冲突的归属校验不变。案例设计里“每条写操作都用会话 token 校验归属”,这个校验独立于增量机制,改造时不要把它和增量逻辑耦合在一起——权限归权限,同步归同步。

一个更低成本的中间态:如果今天还不值得上增量

如果任务量暂时不大,还有一个比全量拉取和增量同步都更轻的折中:先给 /todo/pull 加一个“版本探测”接口,只返回 max(updated_at) 和任务总数。客户端每次轮询先探测,版本没变就跳过拉取,变了再拉全量。它只多一个很便宜的查询,却省掉了大多数轮询的传输量;代价是一次额外的往返。这个方案适合“还没到增量复杂度,但全量传输已经开始浪费”的阶段,也是我在自己排期时会优先落地的步骤:先加探测,再改增量。

验证

需要诚实说明本文的验证边界:

  1. 本文的分析对象是鸿蒙开发实践中心「云同步待办清单」案例的公开设计(案例页与 GitCode 公仓 README,仓库地址:https://gitcode.com/harmony-practice-center/todo_list)。其中提到的架构事实(30 秒轮询、8 个云函数、parseBody / base64 处理方式、ON DUPLICATE KEY UPDATE 的写入方式、冷启动 504 重试等)均来自案例页原文和仓库 README,我在 2026-11-12 当天逐项阅读核对过。
  2. 增量改造的三条边界(软删除、只信服务端游标、毫秒精度)在写作当天通过华为云 AI DevSpace 的 AI Shell 做过一次方案讨论,会话主题为“鸿蒙待办清单增量同步改造分析”,讨论结果与案例原文能互相印证的部分我写进了上文,不能印证的部分我没有写。
  3. 本文没有部署和运行改造后的代码。案例页的“功能检查表”(注册与登录、创建待办、完成/取消、删除后其他设备同步变空列表等)是读者可以按步骤复核的真实清单;增量改造本身也不应只看纸面——如果读者要落地,建议先按“版本探测”这个中间态上线验证,再全量替换。

限制与风险

  • 案例的安全边界要原样保留。案例页明确说明这是教学示范:APIG 认证为 NONE、会话 token 走 URL 查询参数且永不过期、端侧用 Preferences 明文存账号。增量改造不改变这一点,生产化之前应当先做认证和凭据改造,而不是先做同步优化。
  • 软删除会显著放大表体积。墓碑记录需要定期清理,否则 deleted_at 的历史行会无限累积;清理周期要长于客户端可能的最大离线时间,否则长期离线的设备回连后会漏掉删除信号。
  • 本文讨论的“更新”是基于字段级 updated_at 的逻辑删除与覆写。如果多个设备同时改同一条任务的同一字段,依然会出现“后写覆盖先写”的情况;这在待办清单场景下可接受,但在需要冲突合并的场景里,增量同步还需要引入向量时钟或操作日志,这超出了本文范围。
  • 评测数据以外的性能结论本文不做。我没有部署和压测,所以不给出“快多少倍”之类的性能数字;表里的 O(…) 是量级上的分析,不是实测。

结论

  1. 全量轮询在教学和早期阶段是合理的——它用传输量换来了简单、无状态和自愈能力。不要把它当成“低级”方案。
  2. 增量同步值得上的信号是明确的:任务数过几百条、多设备同时轮询、流量或电量成为关注点。改造的最小切面是:updated_at(毫秒精度)+ deleted_at 墓碑 + since 参数 + 服务端返回的 sync_token。
  3. 最容易出错的不是 SQL,而是三条边界:删除必须软删除、游标只信服务端时间、时间戳必须毫秒精度。漏掉任何一条,增量同步会悄悄丢数据,而且比全量模式更难发现问题。
  4. 如果改造排期有限,先上“版本探测”这个中间态;如果确认要全量替换增量,保留登录时拉一次全量的兜底路径。

来源:案例为鸿蒙开发实践中心「云同步待办清单:鸿蒙端云一体实战」(https://harmonybp.developer.huaweicloud.cn/cases/18f755e68ca24c298b5efb8533ef6a44),代码仓 https://gitcode.com/harmony-practice-center/todo_list。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。