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-->