• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 数据库备份与恢复 (ArkTS)
2<!--Kit: ArkData-->
3<!--Subsystem: DistributedDataManager-->
4<!--Owner: @baijidong-->
5<!--Designer: @widecode; @htt1997; @dboy190-->
6<!--Tester: @yippo; @logic42-->
7<!--Adviser: @ge-yafang-->
8
9## 场景介绍
10
11当应用在处理一项重要的操作,显然是不能被打断的。例如:写入多个表关联的事务。此时,每个表的写入都是单独的,但是表与表之间的事务关联性不能被分割。
12
13如果操作的过程中出现问题,开发者可以使用恢复功能,将数据库恢复到之前的状态,重新对数据库进行操作。
14
15在数据库被篡改、删除、或者设备断电场景下,数据库可能会因为数据丢失、数据损坏、脏数据等而不可用,可以通过数据库的备份恢复能力将数据库恢复至可用状态。
16
17
18键值型数据库和关系型数据库均支持对数据库的备份和恢复。另外,键值型数据库还支持删除数据库备份,以释放本地存储空间。
19
20
21## 键值型数据库备份、恢复与删除
22
23键值型数据库,通过backup接口实现数据库备份,通过restore接口实现数据库恢复,通过deletebackup接口删除数据库备份。具体接口及功能,可见[分布式键值数据库](../reference/apis-arkdata/js-apis-distributedKVStore.md)。
24
251. 创建数据库。
26
27   (1) 创建kvManager。
28
29   (2) 配置数据库参数。
30
31   (3) 创建kvStore。
32
33
34   ```ts
35   import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit';
36   import { hilog } from '@kit.PerformanceAnalysisKit';
37   import { distributedKVStore } from '@kit.ArkData';
38   import { BusinessError } from '@kit.BasicServicesKit';
39
40   export default class EntryAbility extends UIAbility {
41     onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
42       this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET);
43       hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
44       let kvManager: distributedKVStore.KVManager;
45       let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;
46       let context = this.context;
47       const kvManagerConfig: distributedKVStore.KVManagerConfig = {
48         context: context,
49         bundleName: 'com.example.datamanagertest'
50       }
51       try {
52         kvManager = distributedKVStore.createKVManager(kvManagerConfig);
53         console.info('Succeeded in creating KVManager.');
54         try {
55           const options: distributedKVStore.Options = {
56             createIfMissing: true,
57             encrypt: true,
58             backup: false,
59             autoSync: false,
60             kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
61             securityLevel: distributedKVStore.SecurityLevel.S3
62           };
63           kvManager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options, (err, store: distributedKVStore.SingleKVStore) => {
64             if (err) {
65               console.error(`Failed to get KVStore. Code:${err.code},message:${err.message}`);
66               return;
67             }
68             console.info('Succeeded in getting KVStore.');
69             kvStore = store;
70             if (kvStore !== undefined) {
71               // 进行后续操作
72               // ...
73             }
74           });
75         } catch (e) {
76           let error = e as BusinessError;
77           console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
78         }
79       } catch (e) {
80         let error = e as BusinessError;
81         console.error(`Failed to create KVManager. Code:${error.code},message:${error.message}`);
82       }
83     }
84   }
85   ```
86
872. 使用put()方法插入数据。
88
89   ```ts
90   const KEY_TEST_STRING_ELEMENT = 'key_test_string';
91   const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
92   try {
93     kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
94       if (err !== undefined) {
95         console.error(`Fail to put data. Code:${err.code},message:${err.message}`);
96         return;
97       }
98       console.info('Succeeded in putting data.');
99     });
100   } catch (e) {
101     let error = e as BusinessError;
102     console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
103   }
104   ```
105
1063. 使用backup()方法备份数据。
107
108   ```ts
109   let backupFile = 'BK001';
110   try {
111     kvStore.backup(backupFile, (err) => {
112       if (err) {
113         console.error(`Fail to backup data.code:${err.code},message:${err.message}`);
114       } else {
115         console.info('Succeeded in backuping data.');
116       }
117     });
118   } catch (e) {
119     let error = e as BusinessError;
120     console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
121   }
122   ```
123
1244. 使用delete()方法删除数据(模拟意外删除、篡改场景)。
125
126   ```ts
127   try {
128     kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {
129       if (err !== undefined) {
130         console.error(`Fail to delete data. Code:${err.code},message:${err.message}`);
131         return;
132       }
133       console.info('Succeeded in deleting data.');
134     });
135   } catch (e) {
136     let error = e as BusinessError;
137     console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
138   }
139   ```
140
1415. 使用restore()方法恢复数据。
142
143   ```ts
144   try {
145     kvStore.restore(backupFile, (err) => {
146       if (err) {
147         console.error(`Fail to restore data. Code:${err.code},message:${err.message}`);
148       } else {
149         console.info('Succeeded in restoring data.');
150       }
151     });
152   } catch (e) {
153     let error = e as BusinessError;
154     console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
155   }
156   ```
157
1586. 当本地设备存储空间有限或需要重新备份时,还可使用deleteBackup()方法删除备份,释放存储空间。
159
160   ```ts
161   let files = [backupFile];
162   try {
163     kvStore.deleteBackup(files).then((data) => {
164       console.info(`Succeed in deleting Backup. Data:filename is ${data[0]},result is ${data[1]}.`);
165     }).catch((err: BusinessError) => {
166       console.error(`Fail to delete Backup. Code:${err.code},message:${err.message}`);
167     })
168   } catch (e) {
169     let error = e as BusinessError;
170     console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
171   }
172   ```
173
174## 关系型数据库备份
175
176数据库操作或者存储过程中,有可能会因为各种原因发生非预期的数据库异常的情况,可以根据需要使用关系型数据库的备份能力,以便在数据库异常时,可靠高效地恢复数据保证业务数据正常使用。
177
178关系型数据库支持手动备份和自动备份(仅系统应用可用)两种方式。
179
180### 手动备份
181
182手动备份:通过调用[backup](../reference/apis-arkdata/arkts-apis-data-relationalStore-RdbStore.md#backup)接口实现数据库手动备份。示例如下:
183
184```ts
185import { UIAbility } from '@kit.AbilityKit';
186import { relationalStore } from '@kit.ArkData';
187import { BusinessError } from '@kit.BasicServicesKit';
188
189export default class EntryAbility extends UIAbility {
190  async onCreate(): Promise<void> {
191    let store: relationalStore.RdbStore | undefined = undefined;
192    let context = this.context;
193
194    const STORE_CONFIG: relationalStore.StoreConfig = {
195      name: 'RdbTest.db',
196      securityLevel: relationalStore.SecurityLevel.S3,
197      allowRebuild: true
198    };
199    try {
200      store = await relationalStore.getRdbStore(context, STORE_CONFIG);
201      await store.executeSql('CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)');
202      console.info('Succeeded in getting RdbStore.');
203    } catch (e) {
204      const err = e as BusinessError;
205      console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
206    }
207
208    if (!store) {
209      return;
210    }
211
212    try {
213      /**
214       * "Backup.db"为备份数据库文件名,默认在RdbStore同路径下备份。
215       * 也可指定绝对路径:"/data/storage/el2/database/Backup.db",文件路径需要存在,不会自动创建目录。
216       */
217      await store.backup("Backup.db");
218      console.info(`Succeeded in backing up RdbStore.`);
219    } catch (e) {
220      const err = e as BusinessError;
221      console.error(`Failed to backup RdbStore. Code:${err.code}, message:${err.message}`);
222    }
223  }
224}
225```
226
227<!--Del-->
228
229### 自动备份(仅系统应用可用)
230
231自动备份:可以通过在[StoreConfig](../reference/apis-arkdata/js-apis-data-relationalStore-sys.md#storeconfig)中配置haMode参数为MAIN_REPLICA实现数据库双写备份,仅支持系统应用。示例如下:
232
233```ts
234import { UIAbility } from '@kit.AbilityKit';
235import { relationalStore } from '@kit.ArkData';
236import { BusinessError } from '@kit.BasicServicesKit';
237
238export default class EntryAbility extends UIAbility {
239  async onCreate(): Promise<void> {
240    let store: relationalStore.RdbStore | undefined = undefined;
241    let context = this.context;
242    try {
243      // 配置StoreConfig的haMode参数为MAIN_REPLICA。
244      const AUTO_BACKUP_CONFIG: relationalStore.StoreConfig = {
245        name: "BackupRestoreTest.db",
246        securityLevel: relationalStore.SecurityLevel.S3,
247        haMode: relationalStore.HAMode.MAIN_REPLICA, // 配置为双写备份
248        allowRebuild: true
249      }
250
251      // 使用getRdbStore()方法创建关系型数据库。
252      store = await relationalStore.getRdbStore(context, AUTO_BACKUP_CONFIG);
253      console.info('Succeeded in getting RdbStore.');
254    } catch (e) {
255      const err = e as BusinessError;
256      console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
257    }
258  }
259}
260```
261
262<!--DelEnd-->
263
264## 关系型数据库异常重建
265
266在创建或使用关系型数据库的过程中,抛出14800011异常错误码说明数据库出现异常,可以删除数据库后恢复数据。
267
268需要通过在[StoreConfig](../reference/apis-arkdata/arkts-apis-data-relationalStore-i.md#storeconfig)中配置allowRebuild参数为true以设置数据库在出现异常时自动删库。数据库重建成功后为空库,需要开发者重新建表并且使用提前备份好的数据进行数据恢复,备份操作可见[关系型数据库备份](#关系型数据库备份),数据恢复可见[关系型数据库恢复](#关系型数据库数据恢复)。
269
270若数据库异常前已配置StoreConfig中的allowRebuild为true,则数据库出现异常时将自动删库。
271
272若数据库异常前未配置StoreConfig中的allowRebuild或allowRebuild配置为false,则需将其配置为true再次进行开库。具体示例如下:
273
274```ts
275import { UIAbility } from '@kit.AbilityKit';
276import { relationalStore } from '@kit.ArkData';
277import { BusinessError } from '@kit.BasicServicesKit';
278
279export default class EntryAbility extends UIAbility {
280  async onCreate(): Promise<void> {
281    let store: relationalStore.RdbStore | undefined = undefined;
282    let context = this.context;
283    try {
284      const STORE_CONFIG: relationalStore.StoreConfig = {
285        name: 'RdbTest.db',
286        securityLevel: relationalStore.SecurityLevel.S3,
287        allowRebuild: true
288      };
289      store = await relationalStore.getRdbStore(context, STORE_CONFIG);
290      await store.executeSql('CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)');
291      console.info('Succeeded in getting RdbStore.');
292    } catch (e) {
293      const err = e as BusinessError;
294      console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
295    }
296  }
297}
298```
299
300## 关系型数据库数据恢复
301
302针对数据库出现异常的情况,在数据库重建成功后,需要用提前备份好的数据进行数据恢复。
303
304恢复方式分以下两种,手动备份恢复和自动备份恢复(仅系统应用可用)。
305
306### 恢复手动备份数据
307
308关系型数据库通过调用backup接口可以实现[手动备份数据库](#手动备份),通过restore接口可以实现手动恢复数据库。
309
310具体恢复过程和关键示例代码片段如下,完整示例代码请结合关系型数据库的备份、重建等上下文进行实现。
311
3121. 抛出数据库异常错误码。
313
314   ```ts
315   let predicates = new relationalStore.RdbPredicates("EMPLOYEE");
316   if (store != undefined) {
317     (store as relationalStore.RdbStore).query(predicates, ["ID", "NAME", "AGE", "SALARY", "CODES"]).then((result: relationalStore.ResultSet) => {
318       let resultSet = result;
319       try {
320         /* ...
321            业务的增删改逻辑
322            ...
323         */
324         // 抛出异常
325         if (resultSet?.rowCount == -1) {
326           resultSet ?.isColumnNull(0);
327         }
328         // todo resultSet.goToFirstRow(), resultSet.count等其它接口也会抛异常
329         while (resultSet.goToNextRow()) {
330           console.info(JSON.stringify(resultSet.getRow()))
331         }
332         resultSet.close();
333       } catch (err) {
334           if (err.code === 14800011) {
335              // 执行下文的步骤,即关闭结果集之后进行数据的恢复
336           }
337           console.error(JSON.stringify(err));
338       }
339     })
340   }
341   ```
342
3432. 关闭所有打开着的结果集。
344
345   ```ts
346   // 获取所有打开着的结果集
347   let resultSets: Array<relationalStore.ResultSet> = [];
348   // 使用resultSet.close()方法关闭所有打开着的结果集
349   for (let resultSet of resultSets) {
350     try {
351       resultSet.close();
352     } catch (e) {
353         if (e.code !== 14800014) {
354           console.error(`Code:${e.code}, message:${e.message}`);
355         }
356     }
357   }
358   ```
359
3603. 调用restore接口恢复数据。
361
362   ```ts
363   import { UIAbility } from '@kit.AbilityKit';
364   import { relationalStore } from '@kit.ArkData';
365   import { BusinessError } from '@kit.BasicServicesKit';
366   import { fileIo } from '@kit.CoreFileKit';
367
368   export default class EntryAbility extends UIAbility {
369     async onCreate(): Promise<void> {
370       let store: relationalStore.RdbStore | undefined = undefined;
371       let context = this.context;
372       let STORE_CONFIG: relationalStore.StoreConfig = {
373         name: "RdbTest.db",
374         securityLevel: relationalStore.SecurityLevel.S3,
375         allowRebuild: true
376       }
377       try {
378         /**
379          * "Backup.db"为备份数据库文件名,默认在当前 store 所在路径下查找备份文件 Backup.db380          * 如在备份时指定了绝对路径:"/data/storage/el2/database/Backup.db", 需要传入绝对路径。
381          */
382         let backupFilePath = context.databaseDir + '/rdb/Backup.db';
383         const backupExist = await fileIo.access(backupFilePath);
384         if (!backupExist) {
385           console.info("Backup is not exist.");
386           // todo 开库建表
387           // todo 自行生成数据
388           return;
389         }
390       } catch (e) {
391         console.error(`Code:${e.code}, message:${e.message}`);
392       }
393
394       try {
395         store = await relationalStore.getRdbStore(context, STORE_CONFIG);
396         // 调用restore接口恢复数据
397         await store.restore("Backup.db");
398         console.log("Restore from back success.")
399       } catch (e) {
400         const err = e as BusinessError;
401         console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
402       }
403     }
404   }
405   ```
406
407<!--Del-->
408
409### 恢复自动备份数据(仅系统应用可用)
410
411关系型数据库,可以通过[restore](../reference/apis-arkdata/js-apis-data-relationalStore-sys.md#restore12)接口恢复[自动备份的数据](#自动备份仅系统应用可用),仅支持系统应用。
412
413关键示例代码片段如下,完整示例代码请结合关系型数据库的备份、重建等上下文进行实现。
414
415   ```ts
416   if (store !== undefined) {
417     try {
418       // 增删改查
419     } catch (err) {
420         if (err.code == 14800011) {
421           // 获取所有打开着的结果集
422           let resultSets: Array<relationalStore.ResultSet> = [];
423           // 使用resultSet.close()方法关闭所有打开着的结果集
424           for (let resultSet of resultSets) {
425             try {
426               resultSet.close();
427             } catch (e) {
428                 if (e.code !== 14800014) {
429                   console.error(`Code:${e.code}, message:${e.message}`);
430                 }
431             }
432           }
433
434           (store as relationalStore.RdbStore).restore("Backup.db", (err: BusinessError) => {
435             if (err) {
436               console.error(`Failed to restore RdbStore. Code:${err.code}, message:${err.message}`);
437               return;
438             }
439             console.info(`Succeeded in restoring RdbStore.`);
440           })
441         }
442         console.error(`Code:${err.code}, message:${err.message}`);
443     }
444   }
445   ```
446
447<!--DelEnd-->
448
449<!--RP1--><!--RP1End-->