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