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.db。 377 * 如在备份时指定了绝对路径:"/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-->