跨设备数据同步功能(即分布式功能),指将数据同步到一个组网环境中的其他设备。常用于用户应用程序数据内容在可信认证的不同设备间,进行自由同步、修改和查询。
例如:当设备1上的应用A在分布式数据库中增、删、改数据后,设备2上的应用A也可以获取到该数据库变化。可在分布式图库、备忘录、联系人、文件管理器等场景中使用。
根据跨设备同步数据生命周期的不同,可以分为:
临时数据生命周期较短,通常保存到内存中。比如游戏应用产生的过程数据,建议使用分布式数据对象。
持久数据生命周期较长,需要保存到存储的数据库中,根据数据关系和特点,可以选择关系型数据库或者键值型数据库。比如图库应用的各种相册、封面、图片等属性信息,建议使用关系型数据库;图库应用的具体图片缩略图,建议使用键值型数据库。
基本概念
在分布式场景中,会涉及多个设备,组网内设备之间看到的数据是否一致称为分布式数据库的一致性。
分布式数据库一致性可以分为强一致性、弱一致性和最终一致性。
强一致性:是指某一设备成功增、删、改数据后,组网内任意设备可立即读取数据获得更新后的值。
弱一致性:是指某一设备成功增、删、改数据后,组网内设备可能读取到本次更新后的数据,也可能读取不到,不能保证在多长时间后每个设备的数据一定是一致的。
最终一致性:是指某一设备成功增、删、改数据后,组网内设备可能读取不到本次更新后的数据,但在某个时间窗口之后组网内设备的数据能够达到一致状态。
强一致性对分布式数据的管理要求非常高,在服务器的分布式场景可能会遇到。因为移动终端设备的不常在线、以及无中心的特性,所以同应用跨设备数据同步不支持强一致性,只支持最终一致性。
跨设备同步访问控制机制
数据跨设备同步时,数据管理基于数据安全标签和设备安全等级进行访问控制。规则为,在本设备的数据安全标签不高于对端设备的设备安全等级时,数据才能从本设备同步到对端设备,否则不能同步。具体访问控制矩阵如下:
设备安全级别 | 可同步的数据安全标签 |
---|---|
SL1 | S1 |
SL2 | S1~S2 |
SL3 | S1~S3 |
SL4 | S1~S4 |
SL5 | S1~S4 |
例如,手表通常为低安全的SL1设备。若创建数据安全标签为S1的数据库,则此数据库数据可以在这些设备间同步;若创建的数据库标签为S2-S4,则不能在这些设备间同步。
键值型数据库跨设备数据同步
键值型数据库适合不涉及过多数据关系和业务关系的业务数据存储,比SQL数据库存储拥有更好的读写性能,同时因其在分布式场景中降低了解决数据库版本兼容问题的复杂度,和数据同步过程中冲突解决的复杂度而被广泛使用。
单版本数据库
单版本是指数据在本地是以单个条目为单位的方式保存,当数据在本地被用户修改时,不管它是否已经被同步出去,均直接在这个条目上进行修改。多个设备全局只保留一份数据,多个设备的相同记录(主码相同)会按时间最新保留一条记录,数据不分设备,设备之间修改相同的key会覆盖。同步也以此为基础,按照它在本地被写入或更改的顺序将当前最新一次修改逐条同步至远端设备,常用于联系人、天气等应用存储场景。
多设备协同数据库
多设备协同分布式数据库建立在单版本数据库之上,对应用程序存入的键值型数据中的Key前面拼接了本设备的DeviceID标识符,这样能保证每个设备产生的数据严格隔离。数据以设备的维度管理,不存在冲突;支持按照设备的维度查询数据。
底层按照设备的维度管理这些数据,多设备协同数据库支持以设备的维度查询分布式数据,但是不支持修改远端设备同步过来的数据。需要分开查询各设备数据的可以使用设备协同版本数据库。常用于图库缩略图存储场景。
手动同步
由应用程序调用sync接口来触发,需要指定同步的设备列表和同步模式。同步模式分为PULL_ONLY(将远端数据拉取到本端)、PUSH_ONLY(将本端数据推送到远端)和PUSH_PULL(将本端数据推送到远端同时也将远端数据拉取到本端)。带有Query参数的同步接口,支持按条件过滤的方法进行同步,将符合条件的数据同步到远端。
自动同步
在跨设备Call调用实现的多端协同场景中,在应用程序更新数据后,由分布式数据库自动将本端数据推送到远端,同时也将远端数据拉取到本端来完成数据同步,应用不需要主动调用sync接口。
数据跨设备同步机制
约束限制
设备协同数据库,针对每条记录,Key的长度≤896 Byte,Value的长度<4 MB。
单版本数据库,针对每条记录,Key的长度≤1 KB,Value的长度<4 MB。
键值型数据库不支持应用程序自定义冲突解决策略。
每个应用程序最多支持同时打开16个键值型分布式数据库。
单个数据库最多支持注册8个订阅数据变化的回调。
接口说明
以下是单版本键值型分布式数据库跨设备数据同步功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,更多接口及使用方式请见分布式键值数据库。
接口名称 | 描述 |
---|---|
createKVManager(config: KVManagerConfig): KVManager | 创建一个KVManager对象实例,用于管理数据库对象。 |
getKVStore<T>(storeId: string, options: Options, callback: AsyncCallback<T>): void | 指定options和storeId,创建并得到指定类型的KVStore数据库。 |
put(key: string, value: Uint8Array | string | number | boolean, callback: AsyncCallback<void>): void | 插入和更新数据。 |
on(event: 'dataChange', type: SubscribeType, listener: Callback<ChangeNotification>): void | 订阅数据库中数据的变化。 |
get(key: string, callback: AsyncCallback<boolean | string | number | Uint8Array>): void | 查询指定Key键的值。 |
sync(deviceIds: string[], mode: SyncMode, delayMs?: number): void | 在手动模式下,触发数据库同步。 |
关系型数据库跨设备数据同步
当应用程序本地存储的关系型数据存在跨设备同步的需求时,可以将需要同步的表数据迁移到新的支持跨设备的表中,当然也可以在刚完成表创建时设置其支持跨设备。
基本概念
关系型数据库跨设备数据同步,支持应用在多设备间同步存储的关系型数据。
应用在数据库中新创建表后,可以设置其为分布式表。在查询远程设备数据库时,根据本地表名可以获取指定远程设备的分布式表名。
设备之间同步数据,数据同步有两种方式,将数据从本地设备推送到远程设备或将数据从远程设备拉至本地设备。
运作机制
底层通信组件完成设备发现和认证,会通知上层应用程序设备上线。收到设备上线的消息后数据管理服务可以在两个设备之间建立加密的数据传输通道,利用该通道在两个设备之间进行数据同步。
数据跨设备同步机制
分布式数据对象跨设备数据同步
传统方式下,设备之间的数据同步,需要开发者完成消息处理逻辑,包括:建立通信链接、消息收发处理、错误重试、数据冲突解决等操作,工作量非常大。而且设备越多,调试复杂度也将同步增加。
其实设备之间的状态、消息发送进度、发送的数据等都是“变量”。如果这些变量支持“全局”访问,那么开发者跨设备访问这些变量就能像操作本地变量一样,从而能够自动高效、便捷地实现数据多端同步。
分布式数据对象即实现了对“变量”的“全局”访问。向应用开发者提供内存对象的创建、查询、删除、修改、订阅等基本数据对象的管理能力,同时具备分布式能力。为开发者在分布式应用场景下提供简单易用的JS接口,轻松实现多设备间同应用的数据协同,同时设备间可以监听对象的状态和数据变更。满足超级终端场景下,相同应用多设备间的数据对象协同需求。与传统方式相比,分布式数据对象大大减少了开发者的工作量。
目前分布式数据对象只能在跨端迁移和通过跨设备Call调用实现的多端协同场景中使用。
基本概念
分布式内存数据库
分布式内存数据库将数据缓存在内存中,以便应用获得更快的数据存取速度,不会将数据进行持久化。若数据库关闭,则数据不会保留。
分布式数据对象
分布式数据对象是一个JS对象型的封装。每一个分布式数据对象实例会创建一个内存数据库中的数据表,每个应用程序创建的内存数据库相互隔离,对分布式数据对象的“读取”或“赋值”会自动映射到对应数据库的get/put操作。
分布式数据对象的生命周期包括以下状态:
- 未初始化:未实例化,或已被销毁。
- 本地数据对象:已创建对应的数据表,但是还无法进行数据同步。
- 分布式数据对象:已创建对应的数据表,设备在线且组网内设置同样sessionId的对象数>=2,可以跨设备同步数据。若设备掉线或将sessionId置为空,分布式数据对象退化为本地数据对象。
运作机制
分布式数据对象生长在分布式内存数据库之上,在分布式内存数据库上进行了JS对象型的封装,能像操作本地变量一样操作分布式数据对象,数据的跨设备同步由系统自动完成。
JS对象型存储与封装机制
为每个分布式数据对象实例创建一个内存数据库,通过SessionId标识,每个应用程序创建的内存数据库相互隔离。
在分布式数据对象实例化的时候,(递归)遍历对象所有属性,使用“Object.defineProperty”定义所有属性的set和get方法,set和get中分别对应数据库一条记录的put和get操作,Key对应属性名,Value对应属性值。
在开发者对分布式数据对象进行“读取”或者“赋值”的时候,都会自动调用到set和get方法,映射到对应数据库的操作。
分布式数据对象,最重要的功能就是对象之间的数据同步。可信组网内的设备可以在本地创建分布式数据对象,并设置sessionID。不同设备上的分布式数据对象,通过设置相同的sessionID,建立对象之间的同步关系。
如下图所示,设备A和设备B上的“分布式数据对象1”,其sessionID均为session1,这两个对象建立了session1的同步关系。
跨设备同步和数据变更通知机制
分布式数据对象,最重要的功能就是对象之间的数据同步。可信组网内的设备可以在本地创建分布式数据对象,并设置sessionID。不同设备上的分布式数据对象,通过设置相同的sessionID,建立对象之间的同步关系。
对象的同步关系
一个同步关系中,一个设备只能有一个对象加入。比如上图中,设备A的“分布式数据对象1”已经加入了session1的同步关系,所以设备A的“分布式数据对象2”就加入失败了。
建立同步关系后,每个Session有一份共享对象数据。加入了同一个Session的对象,支持以下操作:
(1)读取/修改Session中的数据。
(2)监听数据变更,感知其他设备对共享对象数据的修改。
(3)监听状态变更,感知其他设备的加入和退出。
分布式数据对象加入session时,如果它的数据与session中的数据不同,则它会更新session中的数据。如果希望分布式数据对象加入sessionId时不更新session中的数据,并且得到session中的数据,需要将对象的属性的值设置为undefined(资产类型的属性则是将它的各个属性值设置为空字符串)。
同步的最小单位
关于分布式数据对象的数据同步,值得注意的是,同步的最小单位是“属性”。比如,下图中对象1包含三个属性:name、age和parents。当其中一个属性变更时,则数据同步时只需同步此变更的属性。
对象属性支持基本类型(数字类型、布尔类型、字符串类型)以及复杂类型(数组、基本类型嵌套)。针对复杂类型的数据修改,目前仅支持对根属性的修改,暂不支持对下级属性的修改。
- dataObject['parents'] = {mom: "amy"}; // 支持的修改
- dataObject['parents']['mon'] = "amy"; // 不支持的修改
对象持久化缓存机制
分布式对象主要运行在应用程序的进程空间。当调用分布式对象持久化接口时,通过分布式数据库对对象进行持久化和同步,进程退出后数据也不会丢失。
该场景是分布式对象的扩展场景,主要用于以下情况:
在设备上创建持久化对象后APP退出,重新打开APP,创建持久化对象,加入同一个Session,数据可以恢复到APP退出前的数据。
在设备A上创建持久化对象并同步后持久化到设备B后,A设备的APP退出,设备B打开APP,创建持久化对象,加入同一个Session,数据可以恢复到A设备退出前的数据。
约束限制
目前分布式数据对象只能在跨端迁移和通过跨设备Call调用实现的多端协同场景中使用。
不同设备间只有相同bundleName的应用才能直接同步。
分布式数据对象的数据同步发生在同一个应用程序下,且同sessionID之间。
不建议创建过多的分布式数据对象,每个分布式数据对象将占用100-150KB内存。
每个分布式数据对象大小不超过500KB。
设备A修改1KB数据,设备B收到变更通知,50ms内完成。
单个应用程序最多只能创建16个分布式数据对象实例。
考虑到性能和用户体验,最多不超过3个设备进行数据协同。
如对复杂类型的数据进行修改,仅支持修改根属性,暂不支持下级属性修改。资产同步机制中,资产类型的数据支持下一级属性修改。
支持JS接口间的互通,与其他语言不互通。
接口说明
以下是分布式对象跨设备数据同步功能的相关接口,大部分为异步接口。异步接口均有callback和Promise两种返回形式,下表均以callback形式为例,更多接口及使用方式请见分布式数据对象。
接口名称 描述 create(context: Context, source: object): DataObject 创建并得到一个分布式数据对象实例。 genSessionId(): string 创建一个sessionId,可作为分布式数据对象的sessionId。 setSessionId(sessionId: string, callback: AsyncCallback<void>): void 设置同步的sessionId,当可信组网中有多个设备时,多个设备间的对象如果设置为同一个sessionId,就能自动同步。 setSessionId(callback: AsyncCallback<void>): void 退出所有已加入的session。 on(type: 'change', callback: (sessionId: string, fields: Array<string>) => void): void 监听分布式数据对象的数据变更。 off(type: 'change', callback?: (sessionId: string, fields: Array<string>) => void): void 取消监听分布式数据对象的数据变更。 on(type: 'status', callback: (sessionId: string, networkId: string, status: 'online' | 'offline' ) => void): void 监听分布式数据对象的上下线。 off(type: 'status', callback?: (sessionId: string, networkId: string, status: 'online' |'offline' ) => void): void 取消监听分布式数据对象的上下线。 save(deviceId: string, callback: AsyncCallback<SaveSuccessResponse>): void 保存分布式数据对象。 revokeSave(callback: AsyncCallback<RevokeSaveSuccessResponse>): void 撤回保存的分布式数据对象。 bindAssetStore(assetKey: string, bindInfo: BindInfo, callback: AsyncCallback<void>): void 绑定融合资产。