分布式数据对象同步延迟优化与冲突解决
时间: 2026-04-15 11:33:12
(部分内容来自网络,其真实性存疑,为了避免对您造成误导,请谨慎甄别。)
# 分布式数据对象同步延迟优化与冲突解决
一、先说清楚:分布式数据对象是干什么的
分布式数据对象就是一个跑在内存里的JS对象,多台设备设同一个sessionId,改属性就自动同步。
适合:页面标题、当前选中项、开关状态、播放进度、简单表单数据
不适合:大文件、需要持久化的记录、复杂结构化数据
两个限制:
- 跨端迁移场景:单对象 ≤ 150KB
- 多端协同场景:单对象 ≤ 500KB
- 单个应用最多创建16个实例
- 最多3个设备协同
二、最简代码模板
import { distributedDataObject } from '@kit.ArkData';
// 1. 创建对象
const dataObject = distributedDataObject.create(globalThis.getContext(), {
title: '初始标题',
content: '初始内容',
updatedAt: Date.now()
});
// 2. 生成并设置sessionId(同ID的设备自动组网同步)
const sessionId = distributedDataObject.genSessionId();
dataObject.setSessionId(sessionId);
// 3. 监听其他设备的修改
dataObject.on('change', (sessionId: string, fields: Array) => {
console.info('变更的字段:', fields);
// fields是变化的属性名数组,比如['title', 'content']
});
// 4. 监听设备上下线
dataObject.on('status', (sessionId: string, networkId: string, status: string) => {
// status: 'online' 或 'offline'
});
// 5. 修改数据(自动同步到所有同sessionId的设备)
dataObject.title = '新标题';
dataObject.updatedAt = Date.now(); 三、延迟优化
1. 用事件驱动,不要轮询
// ❌ 错误:定时轮询
setInterval(() => {
let val = dataObject.title;
}, 100);
// ✅ 正确:监听change事件
dataObject.on('change', (sessionId, fields) => {
// 收到通知立即处理
});2. 缩小同步粒度
分布式数据对象同步的最小单位是属性,不是整个对象。修改哪个属性就只同步哪个属性。
// ✅ 正确:只改需要的属性 dataObject.title = '新标题'; // 只同步title
// ❌ 错误:整个对象替换 dataObject = { title: '新标题', content: '新内容' }; // 全量同步
3. 大内容单独走资产链路
图片、附件等大内容不要塞进分布式数据对象,用资产同步:
// API version 20+ 支持资产数组同步
dataObject.setAsset('coverImage', '/path/to/image.jpg');
dataObject.setAssets('images', ['/path/1.jpg', '/path/2.jpg']);4. 手动控制sessionId时机
// 页面关闭时退出同步
onPageHide() {
dataObject.setSessionId(''); // 退出session
}
// 需要时重新加入
onPageShow() {
dataObject.setSessionId(sessionId);
}四、冲突解决
分布式数据对象的冲突需要业务层自己处理。底层只负责把变更同步过来,不会自动合并。
方案1:版本号+时间戳(最常用)
type SyncRecord = {
content: string;
version: number;
updatedAt: number;
updatedBy: string;
}
function mergeRecord(local: SyncRecord, remote: SyncRecord): SyncRecord {
// 版本号高的胜出
if (remote.version > local.version) return remote;
if (remote.version < local.version) return local;
// 版本号相同,时间戳晚的胜出
if (remote.updatedAt > local.updatedAt) return remote;
return local;
}
// 在change回调中使用
dataObject.on('change', (sessionId, fields) => {
let merged = mergeRecord(localData, remoteData);
if (merged !== localData) {
// 更新本地
}
});2. 分布式KVStore + 手动冲突处理
如果数据冲突更复杂,建议用KVStore + 自定义合并逻辑:
kvStore.on('dataChange',
distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL,
async (data) => {
for (const entry of data.updateEntries) {
const local = await kvStore.get(entry.key);
const remote = entry.value;
const resolved = customMerge(local, remote); // 你的合并逻辑
if (resolved !== local) {
await kvStore.put(entry.key, resolved);
}
}
}
);3. 三个合并策略
| 策略 | 做法 | 适合场景 |
|---|---|---|
| 最后写入胜出(LWW) | 比较时间戳,晚的覆盖 | 配置、设置 |
| 来源优先级 | 平板 > 手机 > 手表 | 不同设备权重不同 |
| 结构化合并 | 数组合并去重 | 购物清单、标签 |
五、必须满足的前置条件
1. 申请权限:ohos.permission.DISTRIBUTED_DATASYNC
2. 设备登录同一个华为账号
3. 打开Wi-Fi和蓝牙
4. 多设备协同已开启
六、常见问题排查
| 问题 | 可能原因 | 解决 |
|---|---|---|
| 改了属性不触发同步 | sessionId不一致 | 检查两端sessionId是否相同 |
| 收不到change事件 | 没监听或权限问题 | 检查权限和on('change')注册 |
| 延迟大 | 同步粒度过粗 | 只同步变化的属性 |
| 设备上下线不通知 | 没监听status | 加上on('status') |
| 应用重启数据丢失 | 非持久化 | 需要持久化用sve() |
七、持久化:应用重启后恢复数据
分布式数据对象默认在内存里,应用退出就没了。如果需要持久化,调用sve():
// 发起端:保存数据到接收端
dataObject.s**ve(targetDeviceNetworkId, (err, result) => {
if (!err) console.log('保存成功');
});
// 接收端:监听restored状态
dataObject.on('status', (sessionId, networkId, status) => {
if (status === 'restored') {
// 数据已恢复,可以用了
}
});总结一句话:分布式数据对象适合轻量状态同步,延迟优化靠缩小同步粒度+事件驱动,冲突靠版本号/时间戳自己合并。