在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、前置思考

1.1 存储异常是"静默的灾难"

存储异常不像崩溃那样有错误弹窗,往往是悄悄发生、事后才发现

场景1: 用户手机空间不足, 数据库写入失败 → 数据丢失无提示
场景2: 应用被杀进程, 文件写入一半 → 文件损坏
场景3: 存储卡异常/系统更新 → 数据库文件损坏 → 启动崩溃
场景4: 磁盘写满 → 图片保存失败 → 用户以为没点保存

1.2 存储异常的类型

硬件异常: 磁盘坏道、存储卡损坏 (低概率)
系统异常: 空间不足、进程被杀、断电 (中概率)
逻辑异常: 写入中断、版本不兼容、文件被篡改 (高概率)

1.3 本文路线

建立损坏检测、数据恢复、读写重试、空间预警四道防线,让存储异常"可发现、可恢复、可兜底"。

二、核心原理

2.1 文件损坏检测机制

检测手段:
  ① 文件头/魔数校验: 读取固定偏移检查格式标识
  ② 结构完整性: 遍历校验内部结构 (JSON 解析/数据库 integrity_check)
  ③ 校验和: 写入时记录 checksum, 读取时对比
  ④ 长度校验: 元数据记录的预期大小 vs 实际大小

分级检测:
  启动时: 关键文件全量快速校验
  读写时: 每次写入带校验
  周期: 定时巡检重要数据

2.2 数据恢复机制

恢复策略:
  ① 备份恢复: 写入前备份旧文件 (双写/副本)
  ② 冗余恢复: 关键数据双份存储
  ③ 重建恢复: 可重建数据直接重建 (缓存)
  ④ 修复工具: 数据库 integrity_check + 修复导出

恢复优先级:
  P0 用户数据 (不可重建) → 备份/冗余
  P1 业务数据 (半可重建) → 备份 + 修复
  P2 缓存 (可重建) → 直接重建

2.3 读写失败重试

失败分类:
  可重试: IO 忙、空间暂时不足、瞬时错误
  不可重试: 文件不存在、权限拒绝、文件损坏

重试策略:
  指数退避: 100ms → 200ms → 400ms → 上限 5s
  重试次数: 3-5 次
  退避上限: 防止无效占用

2.4 磁盘空间预警

预警等级:
  充足: > 2GB → 正常
  关注: < 2GB → 主动清理缓存
  告警: < 500MB → 停止大文件写入
  危险: < 100MB → 只读模式

获取空间: 系统 API 查询剩余空间/应用占用

三、源码/API 深度解析

3.1 文件完整性校验

import { fileIo as fs } from '@kit.CoreFileKit';
import { cryptoFramework } from '@kit.CryptoArchitectureKit';

// 写入时生成校验和
async function writeWithChecksum(path: string, data: Uint8Array): Promise<void> {
  const md = cryptoFramework.createMd('SHA256');
  await md.update({ data: data });
  const digest = await md.digest();
  const checksum = Array.from(digest.data).map(b => b.toString(16).padStart(2, '0')).join('');

  // 写入数据 + 元数据(checksum, size)
  const file = fs.openSync(path, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.TRUNC);
  fs.writeSync(file.fd, data.buffer);
  fs.closeSync(file.fd);
  // 元数据存 KV: path -> { checksum, size }
}

// 读取时校验
async function readWithVerify(path: string): Promise<Uint8Array | null> {
  try {
    const file = fs.openSync(path, fs.OpenMode.READ_ONLY);
    const stat = fs.statSync(path);
    const buf = new ArrayBuffer(stat.size);
    fs.readSync(file.fd, buf);
    fs.closeSync(file.fd);

    // 重新计算 checksum 对比元数据
    const data = new Uint8Array(buf);
    const checksum = await calcChecksum(data);
    if (checksum !== getMetaChecksum(path)) {
      return null;   // 损坏
    }
    return data;
  } catch (e) {
    return null;
  }
}

3.2 数据库完整性检查与修复

import { relationalStore } from '@kit.ArkData';

// 启动时完整性检查
async function checkDbIntegrity(rdb: relationalStore.RdbStore): Promise<boolean> {
  try {
    const rs = await rdb.querySql('PRAGMA integrity_check');
    let ok = false;
    if (rs.goToFirstRow()) { ok = rs.getString(0) === 'ok'; }
    rs.close();
    return ok;
  } catch (e) {
    return false;
  }
}

// 数据库备份 (写入前)
function backupDb(dbPath: string): string {
  const bak = dbPath + '.bak';
  try { fs.copyFileSync(dbPath, bak); } catch (e) { /* 忽略 */ }
  return bak;
}

// 损坏恢复流程
async function recoverDb(rdbPath: string): Promise<boolean> {
  // 1. 尝试打开备份
  const bak = rdbPath + '.bak';
  if (fs.accessSync(bak)) {
    // 2. 用备份替换损坏库
    fs.unlinkSync(rdbPath);
    fs.copyFileSync(bak, rdbPath);
    return true;
  }
  // 3. 无备份: 尝试导出可读数据重建
  return false;
}

3.3 读写失败重试封装

// 通用重试器
async function withRetry<T>(task: () => Promise<T>,
  retries = 3, baseDelayMs = 100): Promise<T> {
  let lastErr: Error | null = null;
  for (let i = 0; i < retries; i++) {
    try {
      return await task();
    } catch (e) {
      lastErr = e as Error;
      const err = e as { code?: number };
      // 不可重试错误直接抛出
      if (isFatalError(err.code)) { throw e; }
      await sleep(baseDelayMs * Math.pow(2, i));   // 指数退避
    }
  }
  throw lastErr;
}

function isFatalError(code?: number): boolean {
  // 不可重试: 文件不存在(2) / 权限拒绝(13) / 损坏
  return code === 2 || code === 13;
}

3.4 空间预警

import { statfs } from '@kit.CoreFileKit';

interface SpaceInfo {
  freeBytes: number;
  totalBytes: number;
  usedPct: number;
}

function getSpaceInfo(): SpaceInfo {
  // 查询数据目录所在分区的空间
  const stat = statfs('/data');
  const freeBytes = stat.freeBlocks * stat.blockSize;
  const totalBytes = stat.totalBlocks * stat.blockSize;
  return {
    freeBytes: freeBytes,
    totalBytes: totalBytes,
    usedPct: Math.round((totalBytes - freeBytes) / totalBytes * 100)
  };
}

function spaceLevel(info: SpaceInfo): 'OK' | 'LOW' | 'CRITICAL' {
  if (info.freeBytes < 100 * 1024 * 1024) { return 'CRITICAL'; }
  if (info.freeBytes < 500 * 1024 * 1024) { return 'LOW'; }
  return 'OK';
}

四、企业级实战落地

4.1 存储异常防御体系

┌──────────────────────────────────────────┐
│ 预防层:                                   │
│  写入前备份 · 双写冗余 · 磁盘空间监控      │
├──────────────────────────────────────────┤
│ 检测层:                                   │
│  启动完整性检查 · 读写校验和 · 周期巡检    │
├──────────────────────────────────────────┤
│ 恢复层:                                   │
│  备份恢复 → 修复工具 → 重建 → 提示用户     │
├──────────────────────────────────────────┤
│ 兜底层:                                   │
│  重试退避 · 降级模式 · 错误上报            │
└──────────────────────────────────────────┘

4.2 数据库启动检查流程

启动 → 打开数据库
  ├─ integrity_check == ok → 正常使用
  ├─ 损坏 + 有备份 → 恢复备份 → 提示用户
  ├─ 损坏 + 无备份 → 导出可读数据 → 重建 → 提示
  └─ 无法修复 → 只读模式 + 错误上报

4.3 文件写入保护

写入保护规则:
  关键数据: 写临时文件 → fsync → 校验 → rename 覆盖 (原子替换)
  缓存数据: 直接写, 损坏可重建
  图片等: 写入后校验大小 + 文件头

4.4 异常上报与监控

异常 上报内容 处置
文件损坏 路径/类型/校验失败 统计损坏率
空间不足 剩余空间/操作 主动清理
写入失败 错误码/重试次数 定位根因
数据库损坏 版本/备份有无 评估恢复成功率

五、问题排查与性能优化

现象 原因 解决
启动崩溃 数据库打不开 文件损坏 integrity_check + 备份恢复
写入静默失败 数据丢失 无错误处理 写入检查返回值
图片损坏 打不开 写入中断 原子替换 + 校验
空间不足崩溃 保存失败 未监控空间 空间预警 + 降级
重复重试 卡顿 无限重试 指数退避 + 上限
备份失效 恢复失败 备份损坏 双份备份 + 校验
缓存重建风暴 启动全重建 缓存无校验 分级重建

5.1 原子写入(防半写文件)

// 原子替换: 写临时 → 校验 → rename
async function atomicWrite(path: string, data: Uint8Array): Promise<void> {
  const tmpPath = path + '.tmp';
  const file = fs.openSync(tmpPath,
    fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.TRUNC);
  fs.writeSync(file.fd, data.buffer);
  fs.fsyncSync(file.fd);          // 刷盘
  fs.closeSync(file.fd);
  // rename 原子替换 (同文件系统内)
  fs.renameSync(tmpPath, path);
}

5.2 分级恢复策略

P0 用户数据: 双写冗余 (主 + 备份) → 主损坏用备份
P1 业务数据: 定期备份 → 损坏用备份
P2 缓存: 不备份 → 损坏直接重建

5.3 空间预警联动

空间 < 500MB:
  ① 清理 cache 目录
  ② 清理过期文件
  ③ 停止大文件写入
空间 < 100MB:
  ① 进入只读模式
  ② 提示用户清理
  ③ 重要数据立即备份

六、高阶总结与最佳实践

  1. 预防 > 检测 > 恢复:写入前备份、原子替换,把损坏概率降到最低。
  2. 四道检测:文件头、结构校验、checksum、长度——启动与写入时都查。
  3. 分级恢复:P0 双写冗余、P1 定期备份、P2 直接重建。
  4. 重试要退避:指数退避 + 上限,不可重试错误直接抛。
  5. 空间要预警:分级响应,从主动清理到只读模式,避免写入失败静默丢数据。

一句话记住:写前备份、写时校验、坏后恢复、满前预警——存储异常不可怕,可怕的是发现不了、恢复不了、兜底不了。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐