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 { distributedKVStore } from '@kit.ArkData';
31   import { BusinessError } from '@kit.BasicServicesKit';
32
33   let kvManager: distributedKVStore.KVManager;
34   let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;
35   let context = getContext(this);
36   const kvManagerConfig: distributedKVStore.KVManagerConfig = {
37     context: context,
38     bundleName: 'com.example.datamanagertest'
39   }
40   try {
41     kvManager = distributedKVStore.createKVManager(kvManagerConfig);
42     console.info('Succeeded in creating KVManager.');
43     try {
44       const options: distributedKVStore.Options = {
45         createIfMissing: true,
46         encrypt: true,
47         backup: false,
48         autoSync: false,
49         kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
50         securityLevel: distributedKVStore.SecurityLevel.S3
51       };
52       kvManager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options, (err, store: distributedKVStore.SingleKVStore) => {
53         if (err) {
54           console.error(`Failed to get KVStore. Code:${err.code},message:${err.message}`);
55           return;
56         }
57         console.info('Succeeded in getting KVStore.');
58         kvStore = store;
59       });
60     } catch (e) {
61       let error = e as BusinessError;
62       console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
63     }
64   } catch (e) {
65     let error = e as BusinessError;
66     console.error(`Failed to create KVManager. Code:${error.code},message:${error.message}`);
67   }
68
69   if (kvStore !== undefined) {
70     kvStore = kvStore as distributedKVStore.SingleKVStore;
71     //进行后续操作
72     //...
73   }
74   ```
75
762. 使用put()方法插入数据。
77
78   ```ts
79   const KEY_TEST_STRING_ELEMENT = 'key_test_string';
80   const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
81   try {
82     kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
83       if (err !== undefined) {
84         console.error(`Fail to put data. Code:${err.code},message:${err.message}`);
85         return;
86       }
87       console.info('Succeeded in putting data.');
88     });
89   } catch (e) {
90     let error = e as BusinessError;
91     console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
92   }
93   ```
94
953. 使用backup()方法备份数据。
96
97   ```ts
98   let backupFile = 'BK001';
99   try {
100     kvStore.backup(backupFile, (err) => {
101       if (err) {
102         console.error(`Fail to backup data.code:${err.code},message:${err.message}`);
103       } else {
104         console.info('Succeeded in backupping data.');
105       }
106     });
107   } catch (e) {
108     let error = e as BusinessError;
109     console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
110   }
111   ```
112
1134. 使用delete()方法删除数据(模拟意外删除、篡改场景)。
114
115   ```ts
116   try {
117     kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {
118       if (err !== undefined) {
119         console.error(`Fail to delete data. Code:${err.code},message:${err.message}`);
120         return;
121       }
122       console.info('Succeeded in deleting data.');
123     });
124   } catch (e) {
125     let error = e as BusinessError;
126     console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
127   }
128   ```
129
1305. 使用restore()方法恢复数据。
131
132   ```ts
133   try {
134     kvStore.restore(backupFile, (err) => {
135       if (err) {
136         console.error(`Fail to restore data. Code:${err.code},message:${err.message}`);
137       } else {
138         console.info('Succeeded in restoring data.');
139       }
140     });
141   } catch (e) {
142     let error = e as BusinessError;
143     console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
144   }
145   ```
146
1476. 当本地设备存储空间有限或需要重新备份时,还可使用deleteBackup()方法删除备份,释放存储空间。
148
149   ```ts
150   let files = [backupFile];
151   try {
152     kvStore.deleteBackup(files).then((data) => {
153       console.info(`Succeed in deleting Backup. Data:filename is ${data[0]},result is ${data[1]}.`);
154     }).catch((err: BusinessError) => {
155       console.error(`Fail to delete Backup. Code:${err.code},message:${err.message}`);
156     })
157   } catch (e) {
158     let error = e as BusinessError;
159     console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
160   }
161   ```
162
163## 关系型数据库备份
164
165数据库操作或者存储过程中,有可能会因为各种原因发生非预期的数据库损坏等异常情况,可以根据需要使用关系型数据库的备份能力,以便在数据库损坏时,可靠高效地恢复数据保证业务数据正常使用。
166
167关系型数据库支持两种手动备份和自动备份(仅系统应用可用)两种方式。
168
169### 手动备份
170
171手动备份:通过调用[backup](../reference/apis-arkdata/js-apis-data-relationalStore.md#backup)接口实现数据库手动备份。示例如下:
172
173   ```ts
174   import { relationalStore } from '@kit.ArkData';
175   import { BusinessError } from '@kit.BasicServicesKit';
176   import { fileIo } from '@kit.CoreFileKit';
177
178   let store: relationalStore.RdbStore | undefined = undefined;
179
180   let context = getContext(this);
181
182   const STORE_CONFIG: relationalStore.StoreConfig = {
183     name: 'RdbTest.db',
184     securityLevel: relationalStore.SecurityLevel.S3,
185     allowRebuild: true
186   };
187   relationalStore.getRdbStore(context, STORE_CONFIG, (err, rdbStore) => {
188     store = rdbStore;
189     if (err) {
190       console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
191       return;
192     }
193     store.executeSql('CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)', (err) => {
194     })
195     console.info('Succeeded in getting RdbStore.');
196
197     // "Backup.db"为备份数据库文件名,默认在RdbStore同路径下备份。也可指定路径:customDir + "backup.db"
198     (store as relationalStore.RdbStore).backup("Backup.db", (err: BusinessError) => {
199       if (err) {
200         console.error(`Failed to backup RdbStore. Code:${err.code}, message:${err.message}`);
201           return;
202        }
203        console.info(`Succeeded in backing up RdbStore.`);
204     })
205   })
206   ```
207
208<!--Del-->
209
210### 自动备份(仅系统应用可用)
211
212自动备份:可以通过在[StoreConfig](../reference/apis-arkdata/js-apis-data-relationalStore-sys.md#storeconfig)中配置haMode参数为MAIN_REPLICA实现数据库双写备份,仅支持系统应用。示例如下:
213
214   ```ts
215   // 新增StoreConfig配置,配置haMode参数为MAIN_REPLICA。
216   const AUTO_BACKUP_CONFIG :relationalStore.StoreConfig = {
217     name: "BackupResotreTest.db",
218     securityLevel: relationalStore.SecurityLevel.S3,
219     haMode: relationalStore.HAMode.MAIN_REPLICA, // 配置为双写备份
220     allowRebuild: true
221   }
222
223   // 使用getRdbStore()方法创建关系型数据库。
224   relationalStore.getRdbStore(context, AUTO_BACKUP_CONFIG, (err, store) => {
225     if (err) {
226       console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`);
227       return;
228     }
229     console.info('Succeeded in getting RdbStore.');
230   })
231   ```
232
233<!--DelEnd-->
234
235## 关系型数据库损坏重建
236
237在创建或使用关系型数据库的过程中,抛出14800011异常错误码说明数据库发生损坏,此时需要重建数据库并恢复数据。
238
239需要通过在[StoreConfig](../reference/apis-arkdata/js-apis-data-relationalStore.md#storeconfig)中配置allowRebuild参数为true以设置数据库在损坏时自动重建。数据库重建成功后为空库,需要开发者重新建表并且使用提前备份好的数据进行数据恢复,备份操作可见[关系型数据库备份](#关系型数据库备份),数据恢复可见[关系型数据库恢复](#关系型数据库数据恢复)。
240
241若数据库损坏前已配置StoreConfig中的allowRebuild为true,则数据库损坏时将自动重建。
242
243若数据库损坏前未配置StoreConfig中的allowRebuild或allowRebuild配置为false,则需将其配置为true再次进行开库。具体示例如下:
244
245   ```ts
246   import { relationalStore } from '@kit.ArkData';
247   import { BusinessError } from '@kit.BasicServicesKit';
248
249   let store: relationalStore.RdbStore | undefined = undefined;
250
251   let context = getContext(this);
252
253   const STORE_CONFIG: relationalStore.StoreConfig = {
254     name: 'RdbTest.db',
255     securityLevel: relationalStore.SecurityLevel.S3,
256     allowRebuild: true
257   };
258   relationalStore.getRdbStore(context, STORE_CONFIG, (err, rdbStore) => {
259     store = rdbStore;
260     if (err) {
261       console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
262       return;
263     }
264     store.executeSql('CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)', (err) => {
265     })
266     console.info('Succeeded in getting RdbStore.');
267   })
268   ```
269
270## 关系型数据库数据恢复
271
272针对数据库损坏的情况,在数据库重建成功后,需要用提前备份好的数据进行数据恢复。
273
274恢复方式分以下两种,手动备份恢复和自动备份恢复(仅系统应用可用)。
275
276### 恢复手动备份数据
277
278关系型数据库通过调用backup接口可以实现[手动备份数据库](#手动备份),通过restore接口可以实现手动恢复数据库。
279
280具体恢复过程和关键示例代码片段如下,完整示例代码请结合关系型数据库的备份、重建等上下文进行实现。
281
2821. 抛出损坏异常。
283
284   ```ts
285   let predicates = new relationalStore.RdbPredicates("EMPLOYEE");
286   if (store != undefined) {
287     (store as relationalStore.RdbStore).query(predicates, ["ID", "NAME", "AGE", "SALARY", "CODES"]).then((result: relationalStore.ResultSet) => {
288       let resultSet = result;
289       try {
290         /* ...
291            业务的增删改逻辑
292            ...
293         */
294         // 抛出异常
295         if (resultSet?.rowCount == -1) {
296           resultSet ?.isColumnNull(0);
297         }
298         // todo resultSet.goToFirstRow(), resultSet.count等其它接口也会抛异常
299         while (resultSet.goToNextRow()) {
300           console.info(JSON.stringify(resultSet.getRow()))
301         }
302         resultSet.close();
303       } catch (err) {
304           if (err.code === 14800011) {
305              // 执行下文的步骤,即关闭结果集之后进行数据的恢复
306           }
307           console.error(JSON.stringify(err));
308       }
309     })
310   }
311   ```
312
3132. 关闭所有打开着的结果集。
314
315   ```ts
316   // 获取所有打开着的结果集
317   let resultSets: Array<relationalStore.ResultSet> = [];
318   // 使用resultSet.close()方法关闭所有打开着的结果集
319   for (let resultSet of resultSets) {
320     try {
321       resultSet.close();
322     } catch (e) {
323         if (e.code !== 14800014) {
324           console.error(`Code:${e.code}, message:${e.message}`);
325         }
326     }
327   }
328   ```
329
3303. 调用restore接口恢复数据。
331
332   ```ts
333   try {
334     let context = getContext();
335     // "Backup.db"为备份数据库文件名,默认在RdbStore同路径下备份。也可指定路径:customDir + "backup.db"
336     let backup = context.databaseDir + '/backup/test_backup.db';
337     if(!fileIo.access(backup)) {
338       console.info("no backup file");
339       try {
340         (store as relationalStore.RdbStore).close;
341         store = undefined;
342       } catch (e) {
343           if (e.code != 14800014) {
344             console.info(JSON.stringify(e));
345           }
346       }
347       let storeConfig: relationalStore.StoreConfig = {
348         name: "BackupResotreTest.db",
349         securityLevel: relationalStore.SecurityLevel.S3,
350         allowRebuild: true
351       }
352       // todo 开库建表
353       // todo 自行生成数据
354       return
355     }
356     // 调用restore接口恢复数据
357     (store as relationalStore.RdbStore).restore(backup);
358   } catch (e) {
359       console.error(`Code:${e.code}, message:${e.message}`);
360   }
361   ```
362
363<!--Del-->
364
365### 恢复自动备份数据(仅系统应用可用)
366
367关系型数据库,可以通过[restore](../reference/apis-arkdata/js-apis-data-relationalStore-sys.md#restore12)接口恢复[自动备份的数据](#自动备份仅系统应用可用),仅支持系统应用。
368
369关键示例代码片段如下,完整示例代码请结合关系型数据库的备份、重建等上下文进行实现。
370
371   ```ts
372   if (store !== undefined) {
373     try {
374       // 增删改查
375     } catch (err) {
376         if (err.code == 14800011) {
377           // 获取所有打开着的结果集
378           let resultSets: Array<relationalStore.ResultSet> = [];
379           // 使用resultSet.close()方法关闭所有打开着的结果集
380           for (let resultSet of resultSets) {
381             try {
382               resultSet.close();
383             } catch (e) {
384                 if (e.code !== 14800014) {
385                   console.info(`Code:${err.code}, message:${err.message}`);
386                 }
387             }
388           }
389
390           (store as relationalStore.RdbStore).restore("Backup.db", (err: BusinessError) => {
391             if (err) {
392               console.error(`Failed to restore RdbStore. Code:${err.code}, message:${err.message}`);
393               return;
394             }
395             console.info(`Succeeded in restoring RdbStore.`);
396           })
397         }
398         console.info(`Code:${err.code}, message:${err.message}`);
399     }
400   }
401   ```
402
403<!--DelEnd-->