跨端数据漫游,分布式 KVStore 冲突与合并

在做多端协同的购物清单应用时,遇到了一起离奇的"数据丢失"客诉。用户用手机添加了"买牛奶",同时用平板添加了"买面包"。等两台设备联网同步后,手机上的清单里只剩下了面包,牛奶凭空蒸发了。这就是典型的分布式数据同步冲突,系统底层的默认策略把你坑了。

设备发现与隐形权限的门槛

分布式开发的第一道门槛往往不是代码,而是环境。两台设备死活发现不了对方,连底层的软总线都没通。

注意:两台设备必须登录同一个华为账号,且连在同一个 Wi-Fi 下。更关键的是,必须在 module.json5 中显式声明权限,否则系统直接静默拦截你的同步请求。

json

复制代码
{
  "requestPermissions": [
    { "name": "ohos.permission.DISTRIBUTED_DATASYNC" }
  ]
}

这也是个用户级权限(user_grant),必须在运行期弹窗向用户申请。

时间戳机制带来的覆盖灾难

环境通了,我们用 distributedKVStore 存数据。系统默认的同步策略是 Last-Write-Wins(最后写入者胜出),判断标准是设备的系统时间。

这就导致了惨剧:如果手机时间比平板快了 5 分钟,那么手机上产生的任何数据,在同步时都会无条件覆盖平板上修改的数据。如果两人同时在两端操作,弱网环境下数据就会互相抹除。

要解决这个问题,绝不能依赖底层的时间戳,必须在业务层引入版本号(乐观锁)。

typescript

复制代码
interface TaskItem {
  id: string;
  content: string;
  version: number; // 手动维护的版本号
}

版本号机制与手动合并

当监听器收到远端数据推送时,先不着急覆盖 UI,而是对比版本号。

typescript

复制代码
async function handleRemoteSync(remoteData: TaskItem) {
  const localData = await getLocalData(remoteData.id);
  
  if (remoteData.version > localData.version) {
    // 接受远端更新
    saveToLocal(remoteData);
  } else if (remoteData.version === localData.version) {
    // 警示:版本相同但内容不同,发生并发冲突!
    // 业务合并策略:比如把两段文本拼接起来
    const merged = {
      ...remoteData,
      content: localData.content + '\n' + remoteData.content,
      version: localData.version + 1
    };
    saveToLocal(merged);
    // 将合并结果推回远端
    kvStore.put(remoteData.id, merged);
  }
}

通过这套逻辑,两端的数据都能完整保留。

单条数据容量与拆分存储

很多新手为了省事,把几百条清单序列化成一个巨大的 JSON 字符串,塞进一个 Key 里。这是极其致命的。KVStore 单条数据建议不要超过 1MB,否则同步极慢,甚至导致底层崩溃。

必须拆分成细粒度的 Key:item_1, item_2。这样不仅同步快,发生冲突时也能精准到某一条记录去解决,而不是整体覆盖。另外,页面退出前,务必调用 kvStore.off('dataChange') 注销监听,避免回调导致后台耗电。总的来说,分布式同步不能全靠系统,业务层的版本控制和冲突合并才是数据一致性的定海神针。