HarmonyOS 应用开发之数据可靠性与安全性&数据库备份与恢复

概述

功能场景

在系统运行中,存储损坏、存储空间不足、文件系统权限、系统掉电等都可能导致数据库发生故障。比如联系人应用的数据库损坏,导致用户的联系人丢失;日历应用的数据库损坏,导致丢失日历提醒等。为此数据管理提供了数据可靠性与安全性相关的解决方案和能力保障。

  • 备份、恢复功能:重要业务应用(如银行)数据丢失,出现严重异常场景,可以通过备份恢复数据库,保证关键数据不丢失。

  • 数据库加密功能:当数据库中存储如认证凭据、财务数据等高敏感信息时,可对数据库进行加密,提高数据库安全性。

  • 数据库分类分级:数据跨设备同步时,数据管理基于数据安全标签和设备安全等级进行访问控制,保证数据安全。

另外,备份数据库存储在应用的沙箱内,当存储空间不足时,可以选择删除本地的数据库备份,释放空间。

基本概念

在进行数据可靠性与安全性相关功能的开发前,请先了解以下相关概念。

数据库备份与恢复

  • 数据库备份:指对当前数据库的数据库文件进行完整备份。OpenHarmony数据库备份针对数据库全量文件进行完整的备份。
    在进行数据库备份的时候,无需关闭数据库,直接调用对应的数据库备份接口就能完成对数据库文件的备份。

  • 数据库恢复:从指定的备份文件恢复到当前数据库文件。恢复完成时,当前数据库数据恢复到和指定备份文件一致。

数据库加密

数据库加密是对整个数据库文件的加密,可以增强数据库的安全性,有效保护数据库内容。

数据库分类分级

分布式数据管理对数据实施分类分级保护,提供基于数据安全标签以及设备安全等级的访问控制机制。

数据安全标签和设备安全等级越高,加密措施和访问控制措施越严格,数据安全性越高。

运作机制

数据库备份与恢复机制

数据库在备份时,会将当前的数据库备份在指定的文件中,后续对数据库的操作不会影响备份的数据库文件。只有当恢复指定数据库文件时,才会将备份的数据库文件覆盖当前数据库,实现数据的回滚。

  • 键值型数据库备份路径:/data/service/el1(el2)/public/database/...{appId}/kvdb/backup/...{storeId}

  • 关系型数据库备份路径:/data/app/el1(el2)/100/database/...{bundlename}/rdb

数据库加密机制

OpenHarmony数据库加密时,应用开发者无需传入密钥,只需要设置数据库加密的状态即可。系统会自动帮助开发者将数据库加密,使用 huks通用密钥库系统,完成数据库密钥的生成及加密保护。

约束限制

  • 数据库加密的密钥一年自动更换一次。

  • 键值型数据库最多可以备份5份。

  • 键值型数据库的自动备份需要在熄屏且充电的状态下进行。

数据库备份与恢复

场景介绍

当应用在处理一项重要的操作,显然是不能被打断的。例如:写入多个表关联的事务。此时,每个表的写入都是单独的,但是表与表之间的事务关联性不能被分割。

如果操作的过程中出现问题,开发者可以使用恢复功能,将数据库恢复到之前的状态,重新对数据库进行操作。

在数据库被篡改、删除、或者设备断电场景下,数据库可能会因为数据丢失、数据损坏、脏数据等而不可用,可以通过数据库的备份恢复能力将数据库恢复至可用状态。

键值型数据库和关系型数据库均支持对数据库的备份和恢复。另外,键值型数据库还支持删除数据库备份,以释放本地存储空间。

键值型数据库备份、恢复与删除

键值型数据库,通过backup接口实现数据库备份,通过restore接口实现数据库恢复,通过deletebackup接口删除数据库备份。具体接口及功能,可见分布式键值数据库。

  1. 创建数据库。

    (1) 创建kvManager。

    (2) 配置数据库参数。

    (3) 创建kvStore。

import distributedKVStore from '@ohos.data.distributedKVStore';
import { BusinessError } from '@ohos.base';

let kvManager: distributedKVStore.KVManager;
let kvStore: distributedKVStore.SingleKVStore | undefined = undefined;
let context = getContext(this);
const kvManagerConfig: distributedKVStore.KVManagerConfig = {
  context: context,
  bundleName: 'com.example.datamanagertest'
}
try {
  kvManager = distributedKVStore.createKVManager(kvManagerConfig);
  console.info('Succeeded in creating KVManager.');
  try {
    const options: distributedKVStore.Options = {
      createIfMissing: true,
      encrypt: true,
      backup: false,
      autoSync: true,
      kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
      securityLevel: distributedKVStore.SecurityLevel.S1
    };
    kvManager.getKVStore<distributedKVStore.SingleKVStore>('storeId', options, (err, store: distributedKVStore.SingleKVStore) => {
      if (err) {
        console.error(`Failed to get KVStore. Code:${err.code},message:${err.message}`);
        return;
      }
      console.info('Succeeded in getting KVStore.');
      kvStore = store;
    });
  } catch (e) {
    let error = e as BusinessError;
    console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
  }
} catch (e) {
  let error = e as BusinessError;
  console.error(`Failed to create KVManager. Code:${error.code},message:${error.message}`);
}

if (kvStore !== undefined) {
  kvStore = kvStore as distributedKVStore.SingleKVStore;
  //进行后续操作
  //...
}
  1. 使用put()方法插入数据。

    const KEY_TEST_STRING_ELEMENT = 'key_test_string';
    const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
    try {
      kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
        if (err !== undefined) {
          console.error(`Fail to put data. Code:${err.code},message:${err.message}`);
          return;
        }
        console.info('Succeeded in putting data.');
      });
    } catch (e) {
      let error = e as BusinessError;
      console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
    }
    
  2. 使用backup()方法备份数据。

    let file = 'BK001';
    try {
      kvStore.backup(file, (err) => {
        if (err) {
          console.error(`Fail to backup data.code:${err.code},message:${err.message}`);
        } else {
          console.info('Succeeded in backupping data.');
        }
      });
    } catch (e) {
      let error = e as BusinessError;
      console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
    }
    
  3. 使用delete()方法删除数据(模拟意外删除、篡改场景)。

    try {
      kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {
        if (err !== undefined) {
          console.error(`Fail to delete data. Code:${err.code},message:${err.message}`);
          return;
        }
        console.info('Succeeded in deleting data.');
      });
    } catch (e) {
      let error = e as BusinessError;
      console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
    }
    
  4. 使用restore()方法恢复数据。

    let file = 'BK001';
    try {
      kvStore.restore(file, (err) => {
        if (err) {
          console.error(`Fail to restore data. Code:${err.code},message:${err.message}`);
        } else {
          console.info('Succeeded in restoring data.');
        }
      });
    } catch (e) {
      let error = e as BusinessError;
      console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
    }
    
  5. 当本地设备存储空间有限或需要重新备份时,还可使用deleteBackup()方法删除备份,释放存储空间。

    let files = ['BK001'];
    try {
      kvStore.deleteBackup(files).then((data) => {
        console.info(`Succeed in deleting Backup. Data:filename is ${data[0]},result is ${data[1]}.`);
      }).catch((err: BusinessError) => {
        console.error(`Fail to delete Backup. Code:${err.code},message:${err.message}`);
      })
    } catch (e) {
      let error = e as BusinessError;
      console.error(`An unexpected error occurred. Code:${error.code},message:${error.message}`);
    }
    

关系型数据库备份与恢复

关系型数据库,通过backup接口实现数据库备份,通过restore接口实现数据库恢复。具体接口及功能,可见关系型数据库。

  1. 使用getRdbStore()方法创建关系型数据库。

    import relationalStore from '@ohos.data.relationalStore';
    import { BusinessError } from '@ohos.base';
    
    let store: relationalStore.RdbStore | undefined = undefined;
    
    let context = getContext(this);
    
    const STORE_CONFIG: relationalStore.StoreConfig = {
      name: 'RdbTest.db',
      securityLevel: relationalStore.SecurityLevel.S1
    };
    relationalStore.getRdbStore(context, STORE_CONFIG, (err, rdbStore) => {
      store = rdbStore;
      if (err) {
        console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
        return;
      }
      store.executeSql('CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)', (err) => {
      })
      console.info('Succeeded in getting RdbStore.');
    })
    
  2. 使用insert()方法插入数据。

    import { ValuesBucket } from '@ohos.data.ValuesBucket';
    
    let value1 = 'Rose';
    let value2 = 18;
    let value3 = 100.5;
    let value4 = new Uint8Array([1, 2, 3, 4, 5]);
    
    // 以下三种方式可用
    const valueBucket1: ValuesBucket = {
      'NAME': value1,
      'AGE': value2,
      'SALARY': value3,
      'CODES': value4,
    };
    const valueBucket2: ValuesBucket = {
      NAME: value1,
      AGE: value2,
      SALARY: value3,
      CODES: value4,
    };
    const valueBucket3: ValuesBucket = {
      "NAME": value1,
      "AGE": value2,
      "SALARY": value3,
      "CODES": value4,
    };
    
    if(store != undefined) {
      (store as relationalStore.RdbStore).insert('EMPLOYEE', valueBucket1, relationalStore.ConflictResolution.ON_CONFLICT_REPLACE, (err, rowId) => {
        if (err) {
          console.error(`Failed to insert data. Code:${err.code},message:${err.message}`);
          return;
        }
        console.info(`Succeeded in inserting data. rowId:${rowId}`);
      })
    }
    
  3. 使用backup()方法备份数据。

    if(store != undefined) {
      (store as relationalStore.RdbStore).backup('dbBackup.db', (err) => {
        if (err) {
          console.error(`Failed to backup data. Code:${err.code},message:${err.message}`);
          return;
        }
        console.info('Succeeded in backuping data.');
      })
    }
    
  4. 使用delete()方法删除数据(模拟意外删除、篡改场景)。

    let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
    predicates.equalTo('NAME', 'Lisa');
    if(store != undefined) {
      (store as relationalStore.RdbStore).delete(predicates).then((rows: number) => {
        console.info(`Delete rows: ${rows}`);
      }).catch((err: BusinessError) => {
        console.error(`Failed to delete data. Code:${err.code},message:${err.message}`);
      })
    }
    
  5. 使用restore()方法恢复数据。

    if(store != undefined) {
      (store as relationalStore.RdbStore).restore('dbBackup.db', (err) => {
        if (err) {
          console.error(`Failed to restore data. Code:${err.code},message:${err.message}`);
          return;
        }
        console.info('Succeeded in restoring data.');
      })
    }
    

写在最后

  • 如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习知识点,请移步前往小编:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容