鸿蒙 HarmonyOS 6 | 分布式数据同步详解
跨设备数据同步常见的落点主要有两类。一类是分布式数据对象,适合做轻量状态联动;另一类是分布式 KVStore,适合做更接近存储和同步控制的数据管理。只要把这两类东西混在一起谈,后面的延迟和冲突判断就很容易失真。
前言
做跨设备协同的时候,很多人最先感知到的问题就是延迟。手机改了一次状态,平板没有马上更新;两台设备几乎同时改了同一份数据,最后显示结果又不一样。
表面看像是同步太慢,往下拆才会发现,真正的问题通常不是单一的性能瓶颈,而是同步模型、数据粒度、组网状态和业务冲突一起叠出来的结果。
跨设备数据同步常见的落点主要有两类。一类是分布式数据对象,适合做轻量状态联动;另一类是分布式 KVStore,适合做更接近存储和同步控制的数据管理。只要把这两类东西混在一起谈,后面的延迟和冲突判断就很容易失真。

一、先把同步入口分清楚
分布式数据对象更适合那种轻量、结构明确、状态变化频繁的数据。比如标题、当前选中项、简单表单状态、页面联动信息。它的核心动作其实不复杂,创建对象,生成 sessionId,把对象挂进同一个会话,再监听 change 和 status。这样一来,同一个会话里的多台设备就会围绕这份对象状态同步变化。
下面这个示例更接近实际项目里该怎么写。它不是把分布式数据对象当成一个抽象概念来讲,而是把创建、监听和状态更新这几步都落到了代码里。
import { distributedDataObject } from '@kit.ArkData';
type NoteState = {
title: string;
content: string;
updatedAt: number;
};
const noteObject = distributedDataObject.create(globalThis.getContext(), {
title: '初始标题',
content: '初始内容',
updatedAt: Date.now()
} as NoteState);
// 生成并设置同一组设备共享的 sessionId
const sessionId = distributedDataObject.genSessionId();
noteObject.setSessionId(sessionId);
// 监听远端变更
noteObject.on('change', (sessionId: string, changedData: Record<string, Object>) => {
console.info(`change received, sessionId=${sessionId}`);
console.info(`changed fields=${JSON.stringify(changedData)}`);
});
// 监听设备在线状态变化
noteObject.on('status', (sessionId: string, networkId: string, status: string) => {
console.info(`status changed, sessionId=${sessionId}, networkId=${networkId}, status=${status}`);
});
// 本地修改会触发跨设备同步
noteObject.title = '更新后的标题';
noteObject.updatedAt = Date.now();
这段代码的重点不在于写法多复杂,而在于它准确落在当前能力范围内。分布式数据对象适合共享状态,但它不是协同编辑引擎,也不是拿来同步大块内容的通用通道。
如果你的场景更像待办、配置、结构化记录同步,而不是状态对象联动,那么 KVStore 会更合适。KVStore 这条线支持自动同步和手动同步,也支持通过 on('dataChange') 监听跨设备数据变化。同步模式本身也分 PULL_ONLY、PUSH_ONLY 和 PUSH_PULL,这比直接把所有变化都塞进状态对象里要稳得多。
二、延迟问题很多时候是同步粒度错了
跨设备同步最容易踩的坑,不是设备多,而是同步单元太大。你把一整份文档、整块复杂对象、甚至图片和文本一起当成一次普通状态更新来处理,这时感知出来的延迟一定不会好看。因为对系统来说,你并不是在同步一份轻量状态,而是在拿状态同步通道传大块内容。
更稳的做法通常是把轻量状态和重资源拆开。文本状态、选中状态、版本号这类东西走分布式数据对象或者 KVStore。图片、附件、文件内容这类东西走数据资产或者分布式文件路径。内容编辑的最佳实践里,本身就把图片复制到分布式文件目录,再通过数据对象去联动状态,而不是把所有东西都塞进一个同步对象里。
如果一定要给项目里留一层策略判断,那这层策略也应该是应用自己的数据建模逻辑,而不是把平台写成自带优先级调度和实时专线。
type SyncPath = 'dataObject' | 'kvStore' | 'asset';
function chooseSyncPath(dataType: string, payloadSize: number): SyncPath {
if (dataType === 'ui_state' || dataType === 'page_state') {
return 'dataObject';
}
if (dataType === 'record' || dataType === 'settings') {
return 'kvStore';
}
if (payloadSize > 10 * 1024 || dataType === 'image' || dataType === 'file') {
return 'asset';
}
return 'kvStore';
}
这里表达的是应用自己的同步路径选择,不是在伪造一套平台内置的 QoS 系统。
三、冲突问题真正难的地方在业务层
跨设备同步里最麻烦的,不是收不到变更,而是两边都收到了变更,结果还彼此打架。分布式数据对象可以通过 change 告诉你哪些字段变了,KVStore 可以通过 dataChange 告诉你数据发生了变化,但这些能力解决的是通知,不是合并。两个设备几乎同时改同一条记录,到底保留谁,怎么合并,怎么提示用户,这件事最终还是得回到业务层自己处理。
所以更稳的做法不是去找一个万能冲突策略,而是把数据结构拆小。比如把列表按条目同步,把文档按块同步,把重要记录带上 updatedAt 或版本号,不要每次都整份覆盖。只要同步单元够粗,一旦设备数上来、写入变频繁,冲突就一定会变得难解释。
四、KVStore 更适合做可控的跨设备同步
如果你的场景更偏数据记录而不是状态对象,那么 KVStore 这条线更稳。它的优势不在于更快,而在于同步触发点更清楚。你可以决定哪些写操作走自动同步,哪些时机手动发起同步,也可以明确指定同步目标设备和同步模式。
import { distributedKVStore } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
const kvManager = distributedKVStore.createKVManager({
context: globalThis.getContext(),
bundleName: 'com.example.distributedapp'
});
const kvStore = await kvManager.getKVStore<distributedKVStore.SingleKVStore>('user_settings', {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S1
});
// 监听远端数据变化
kvStore.on('dataChange', 0, (dataChange) => {
console.info(`insert entries=${JSON.stringify(dataChange.insertEntries)}`);
console.info(`update entries=${JSON.stringify(dataChange.updateEntries)}`);
console.info(`delete entries=${JSON.stringify(dataChange.deleteEntries)}`);
});
// 写入结构化数据
await kvStore.put('note_meta', JSON.stringify({
title: '会议纪要',
updatedAt: Date.now()
}));
// 手动同步示意
function syncToDevices(deviceIds: string[]) {
if (!deviceIds.length) {
return;
}
kvStore.sync(deviceIds, distributedKVStore.SyncMode.PUSH_PULL);
}
这段代码里最值得注意的地方有两个。一个是 autoSync 和手动 sync 是两种不同控制方式,不要混成一个概念。另一个是 sync 需要目标设备列表,设备标识通常来自设备管理侧拿到的 networkId。也就是说,真正的跨设备同步不是只盯着数据库本身,还得把组网状态一起考虑进去。
五、真正值得先优化的是同步边界
如果项目还在第一版,最值得优先做的事,不是追着极限低延迟跑,而是把同步边界划清楚。哪些是状态对象,哪些是结构化记录,哪些是大文件资产,哪些场景下允许自动同步,哪些场景必须显式触发同步。只要边界清楚,后面的调试成本会低很多。
很多同步问题一开始看起来像框架不稳定,往下追才发现,是轻量状态、存储记录和大资源都被塞进了一条同步链路里。框架并没有替你完成业务建模,它只是给了你几块可以组合使用的同步能力。真正决定体验的,还是你怎么拆数据,怎么选同步路径,怎么设计冲突处理。
总结
如果只用一句话概括鸿蒙 6 的分布式数据同步现状,那就是能力已经够做事了,但前提是不要把所有同步问题都丢给同一条链路。轻量状态适合分布式数据对象,结构化记录更适合 KVStore,大资源应该拆到资产或文件路径里,真正复杂的冲突合并要回到业务层自己设计。
这样一来,延迟和冲突就不再是一团笼统的同步问题,而会变成几个可以逐层拆开的工程问题。
更多推荐



所有评论(0)