【OpenHarmony应用开发】分布式数据服务
分布式数据服务概述
分布式数据服务(Distributed Data Service,DDS) 为应用程序提供不同设备间数据库的分布式协同能力。通过调用分布式数据接口,应用程序将数据保存到分布式数据库中。通过结合帐号、应用和数据库三元组,分布式数据服务对属于不同应用的数据进行隔离,保证不同应用之间的数据不能通过分布式数据服务互相访问。在通过可信认证的设备间,分布式数据服务支持应用数据相互同步,为用户提供在多种终端设备上最终一致的数据访问体验。
基本概念
-
KV数据模型
“KV数据模型”是“Key-Value数据模型”的简称,“Key-Value”即“键-值”;其数据以键值对的形式进行组织、索引和存储。KV数据模型适合不涉及过多数据关系和业务关系的业务数据存储,比SQL数据库存储拥有更好的读写性能,同时因其在分布式场景中降低了解决数据库版本兼容问题的复杂度,和数据同步过程中冲突解决的复杂度而被广泛使用。分布式数据库也是基于KV数据模型,对外提供KV类型的访问接口。
-
分布式数据库事务性
分布式数据库事务支持本地事务(和传统数据库的事务概念一致)和同步事务。同步事务是指在设备之间同步数据时,以本地事务为单位进行同步,一次本地事务的修改要么都同步成功,要么都同步失败。 -
分布式数据库一致性
在分布式场景中一般会涉及多个设备,组网内设备之间看到的数据是否一致称为分布式数据库的一致性。分布式数据库一致性可以分为强一致性、弱一致性和最终一致性。- 强一致性:是指某一设备成功增、删、改数据后,组网内设备对该数据的读取操作都将得到更新后的值。
- 弱一致性:是指某一设备成功增、删、改数据后,组网内设备可能读取到本次更新数据,也可能读取不到,不能保证在多长时间后每个设备的数据一定是一致的。
- 最终一致性:是指某一设备成功增、删、改数据后,组网内设备可能读取不到本次更新数据,但在某个时间窗口之后组网内设备的数据能够达到一致状态。
强一致性对分布式数据的管理要求非常高,在服务器的分布式场景可能会遇到。因为移动终端设备的不常在线、以及无中心的特性,分布式数据服务不支持强一致性,只支持最终一致性。
-
分布式数据库同步
底层通信组件完成设备发现和认证,会通知上层应用程序(包括分布式数据服务)设备上线。收到设备上线的消息后分布式数据服务可以在两个设备之间建立加密的数据传输通道,利用该通道在两个设备之间进行数据同步。分布式数据服务提供了两种同步方式:手动同步和自动同步。
- 手动同步:由应用程序调用sync接口来触发,需要指定同步的设备列表和同步模式。同步模式分为PULL_ONLY(将远端数据拉到本端)、PUSH_ONLY(将本端数据推送到远端)和PUSH_PULL(将本端数据推送到远端同时也将远端数据拉取到本端)。内部接口支持按条件过滤同步,将符合条件的数据同步到远端。
- 自动同步:包括全量同步和按条件订阅同步。全量同步由分布式数据库自动将本端数据推送到远端,同时也将远端数据拉取到本端来完成数据同步,同步时机包括设备上线、应用程序更新数据等,应用不需要主动调用sync接口;内部接口支持按条件订阅同步,将远端符合订阅条件的数据自动同步到本端。
-
单版本分布式数据库
单版本是指数据在本地保存是以单个KV条目为单位的方式保存,对每个Key最多只保存一个条目项,当数据在本地被用户修改时,不管它是否已经被同步出去,均直接在这个条目上进行修改。同步也以此为基础,按照它在本地被写入或更改的顺序将当前最新一次修改逐条同步至远端设备。 -
设备协同分布式数据库
设备协同分布式数据库建立在单版本分布式数据库之上,对应用程序存入的KV数据中的Key前面拼接了本设备的DeviceID标识符,这样能保证每个设备产生的数据严格隔离,底层按照设备的维度管理这些数据,设备协同分布式数据库支持以设备的维度查询分布式数据,但是不支持修改远端设备同步过来的数据。 -
分布式数据库冲突解决策略
分布式数据库多设备提交冲突场景,在给提交冲突做合并的过程中,如果多个设备同时修改了同一数据,则称这种场景为数据冲突。数据冲突采用默认冲突解决策略(Last-write-wins),基于提交时间戳,取时间戳较大的提交数据,当前不支持定制冲突解决策略。 -
数据库Schema化管理与谓词查询
单版本数据库支持在创建和打开数据库时指定Schema,数据库根据Schema定义感知KV记录的Value格式,以实现对Value值结构的检查,并基于Value中的字段实现索引建立和谓词查询。 -
分布式数据库备份能力
提供分布式数据库备份能力,业务通过设置backup属性为true,可以触发分布式数据服务每日备份。当分布式数据库发生损坏,分布式数据服务会删除损坏数据库,并且从备份数据库中恢复上次备份的数据。如果不存在备份数据库,则创建一个新的数据库。同时支持加密数据库的备份能力。
运作机制
分布式数据服务支撑OpenHarmony系统上应用程序数据库数据分布式管理,支持数据在相同帐号的多端设备之间相互同步,为用户在多端设备上提供一致的用户体验,分布式数据服务包含五部分:
-
服务接口
分布式数据服务提供专门的数据库创建、数据访问、数据订阅等接口给应用程序调用,接口支持KV数据模型,支持常用的数据类型,同时确保接口的兼容性、易用性和可发布性。 -
服务组件
服务组件负责服务内元数据管理、权限管理、加密管理、备份和恢复管理以及多用户管理等、同时负责初始化底层分布式DB的存储组件、同步组件和通信适配层。 -
存储组件
存储组件负责数据的访问、数据的缩减、事务、快照、数据库加密,以及数据合并和冲突解决等特性。 -
同步组件
同步组件连结了存储组件与通信组件,其目标是保持在线设备间的数据库数据一致性,包括将本地产生的未同步数据同步给其他设备,接收来自其他设备发送过来的数据,并合并到本地设备中。 -
通信适配层
通信适配层负责调用底层公共通信层的接口完成通信管道的创建、连接,接收设备上下线消息,维护已连接和断开设备列表的元数据,同时将设备上下线信息发送给上层同步组件,同步组件维护连接的设备列表,同步数据时根据该列表,调用通信适配层的接口将数据封装并发送给连接的设备。
应用程序通过调用分布式数据服务接口实现分布式数据库创建、访问、订阅功能,服务接口通过操作服务组件提供的能力,将数据存储至存储组件,存储组件调用同步组件实现将数据同步,同步组件使用通信适配层将数据同步至远端设备,远端设备通过同步组件接收数据,并更新至本端存储组件,通过服务接口提供给应用程序使用。
图1 数据分布式运作示意图
约束与限制
-
分布式数据服务的数据模型仅支持KV数据模型,不支持外键、触发器等关系型数据库中的功能。
-
分布式数据服务支持的KV数据模型规格:
- 设备协同数据库,针对每条记录,Key的长度≤896 Byte,Value的长度<4 MB。
- 单版本数据库,针对每条记录,Key的长度≤1 KB,Value的度<4 MB。
- 每个应用程序最多支持同时打开16个分布式数据库。
-
分布式数据库与本地数据库的使用场景不同,因此开发者应识别需要在设备间进行同步的数据,并将这些数据保存到分布式数据库中。
-
分布式数据服务当前不支持应用程序自定义冲突解决策略。
-
分布式数据服务针对每个应用程序当前的流控机制:KvStore的接口1秒最大访问1000次,1分钟最大访问10000次;KvManager的接口1秒最大访问50次,1分钟最大访问500次。
-
分布式数据库事件回调方法中不允许进行阻塞操作,例如修改UI组件。
分布式数据服务开发指导
场景介绍
分布式数据服务主要实现用户设备中应用程序数据内容的分布式同步。当设备1上的应用A在分布式数据库中增、删、改数据后,设备2上的应用A也可以获取到该数据库变化。可在分布式图库、信息、通讯录、文件管理器等场景中使用。
接口说明
OpenHarmony系统中的分布式数据服务模块为开发者提供下面几种功能:
表1 分布式数据服务关键API功能介绍
功能分类 | 接口名称 | 描述 |
---|---|---|
分布式数据库创建。 | createKVManager(config: KVManagerConfig, callback: AsyncCallback): void createKVManager(config: KVManagerConfig): Promise |
创建一个KVManager对象实例,用于管理数据库对象。 |
分布式数据库创建。 | getKVStore(storeId: string, options: Options, callback: AsyncCallback): void getKVStore(storeId: string, options: Options): Promise |
指定Options和storeId,创建并获取KVStore数据库。 |
分布式数据增、删、改、查。 | put(key: string, value: Uint8Array | string | number | boolean, callback: AsyncCallback): void put(key: string, value: Uint8Array | string | number | boolean): Promise |
插入和更新数据。 |
分布式数据增、删、改、查。 | delete(key: string, callback: AsyncCallback): void delete(key: string): Promise |
删除数据。 |
分布式数据增、删、改、查。 | get(key: string, callback: AsyncCallback): void get(key: string): Promise |
查询数据。 |
订阅分布式数据变化。 | on(event: ‘dataChange’, type: SubscribeType, observer: Callback): void on(event: ‘syncComplete’, syncCallback: Callback<Array>): void |
订阅数据库中数据的变化。 |
分布式数据同步。 | sync(deviceIdList: string[], mode: SyncMode, allowedDelayMs?: number): void | 在手动模式下,触发数据库同步。 |
开发步骤
以单版本分布式数据库为例,说明开发步骤。
-
导入模块。
import distributedData from '@ohos.data.distributedData';
-
根据配置构造分布式数据库管理类实例。
- 根据应用上下文创建KvManagerConfig对象。
- 创建分布式数据库管理器实例。
以下为创建分布式数据库管理器的代码示例:
let kvManager;try { const kvManagerConfig = { bundleName : 'com.example.datamanagertest', userInfo : { userId : '0', userType : distributedData.UserType.SAME_USER_ID } } distributedData.createKVManager(kvManagerConfig, function (err, manager) { if (err) { console.log("createKVManager err: " + JSON.stringify(err)); return; } console.log("createKVManager success"); kvManager = manager; });} catch (e) { console.log("An unexpected error occurred. Error:" + e);}
-
获取/创建分布式数据库。
- 声明需要创建的分布式数据库ID描述。
- 创建分布式数据库,建议关闭自动同步功能(autoSync:false),需要同步时主动调用sync接口。
以下为创建分布式数据库的代码示例:
let kvStore;try { const options = { createIfMissing : true, encrypt : false, backup : false, autoSync : false, kvStoreType : distributedData.KVStoreType.SINGLE_VERSION, securityLevel : distributedData.SecurityLevel.S0, }; kvManager.getKVStore('storeId', options, function (err, store) { if (err) { console.log("getKVStore err: " + JSON.stringify(err)); return; } console.log("getKVStore success"); kvStore = store; });} catch (e) { console.log("An unexpected error occurred. Error:" + e);}
说明:
组网设备间同步数据的场景,建议在应用启动时打开分布式数据库,获取数据库的句柄。在该句柄(如上例中的kvStore)的生命周期内无需重复创建数据库,可直接使用句柄对数据库进行数据的插入等操作。
-
订阅分布式数据变化。
以下为订阅单版本分布式数据库数据变化通知的代码示例:kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, function (data) { console.log("dataChange callback call data: " + JSON.stringify(data));});
-
将数据写入分布式数据库。
- 构造需要写入分布式数据库的Key(键)和Value(值)。
- 将键值数据写入分布式数据库。
以下为将字符串类型键值数据写入分布式数据库的代码示例:
const KEY_TEST_STRING_ELEMENT = 'key_test_string';const VALUE_TEST_STRING_ELEMENT = 'value-test-string';try { kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, function (err,data) { if (err != undefined) { console.log("put err: " + JSON.stringify(err)); return; } console.log("put success"); });}catch (e) { console.log("An unexpected error occurred. Error:" + e);}
-
查询分布式数据库数据。
- 构造需要从单版本分布式数据库中查询的Key(键)。
- 从单版本分布式数据库中获取数据。
以下为从分布式数据库中查询字符串类型数据的代码示例:
const KEY_TEST_STRING_ELEMENT = 'key_test_string';const VALUE_TEST_STRING_ELEMENT = 'value-test-string';try { kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, function (err,data) { if (err != undefined) { console.log("put err: " + JSON.stringify(err)); return; } console.log("put success"); kvStore.get(KEY_TEST_STRING_ELEMENT, function (err,data) { console.log("get success data: " + data); }); });}catch (e) { console.log("An unexpected error occurred. Error:" + e);}
-
同步数据到其他设备。
1.选择同一组网环境下的设备以及同步模式,进行数据同步。以下为单版本分布式数据库进行数据同步的代码示例,其中deviceIds可由deviceManager调用getTrustedDeviceListSync()方法得到,1000表示最大延迟时间为1000ms:
import deviceManager from '@ohos.distributedHardware.deviceManager';let devManager;// create deviceManagerdeviceManager.createDeviceManager("bundleName", (err, value) => { if (!err) { devManager = value; // get deviceIds let deviceIds = []; if (devManager != null) { var devices = devManager.getTrustedDeviceListSync(); for (var i = 0; i < devices.length; i++) { deviceIds[i] = devices[i].deviceId; } } try{ kvStore.sync(deviceIds, distributedData.SyncMode.PUSH_ONLY, 1000); }catch (e) { console.log("An unexpected error occurred. Error:" + e); } }});