更新记录
1.0.0(2026-11-05)
首个版本:鸿蒙 relationalStore SQLite 内核 + 防回退双副本自愈(官方 backup 副本 / filesDir 耐久副本 / seq 三方裁决自动恢复);提交零阻塞(1.2s 防抖后台维护 + seq 内存缓存);UTS Promise 边界包装规避链式吞值;onDiag 诊断全程留痕。
平台兼容性
uni-app x(5.25)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | × | × | 12 | × |
鸿蒙 SQLite 防回退(杀后台数据不丢)· 双副本自愈本地数据库
鸿蒙本地数据库 UTS 插件(app-harmony,API 12+),内置对抗鸿蒙系统级「杀后台整目录回退」的双副本自愈机制:官方 backup 副本 + filesDir 耐久副本 + seq 三方裁决 + 库外提交标记,提交链零阻塞。真机实测:杀后台 → 重进,数据不丢。
一个真实的事故(本插件的由来)
这是从一个日活记账 App 的真实线上事故中打磨出来的方案,不是实验室代码:
用户反馈「记账数据全没了」。文件级取证(把设备沙盒里的数据库文件直接拉出来解析)发现:写盘根本没坏,每一笔都提交成功。真凶是——
鸿蒙 relationalStore 在进程被杀后台后重开时,会把整个 rdb 目录(主库 + WAL + 官方 backup 出的 .bak)回退到本次会话开始前的旧快照。 已提交的数据直接消失。
PRAGMA wal_checkpoint无效——回退发生在 relationalStore 服务层,不在 SQLite 层;- 官方
backup()出的副本与主库同目录,一起被回退;- 断网时没有服务器同步兜底,用户看到的就是「记的数据全没了」。
这个行为不报错、不留日志、只在「杀后台 + 断网」组合下出现,是最隐蔽的一类数据丢失。市面上的鸿蒙 SQLite 插件都是 relationalStore 的能力封装,都不处理这个问题——因为不亲身踩一遍根本不知道它存在。
与常见 SQLite 插件的定位区别
| 常见 SQLite 插件 | 本插件 | |
|---|---|---|
| 基础 SQLite 能力 | ✓ | ✓(relationalStore,列类型原生保真) |
| 杀后台整目录回退 | ✗(无处理,回退照丢) | ✓ 自动裁决 + 自动恢复 |
| 副本防呆 | ✗(backup 异步落盘可能拷出空副本) | ✓ 尺寸守卫 + 原子拷贝 + 延迟重试 |
| 提交性能 | 取决于用法(同步等备份会卡提交) | ✓ 防抖后台维护,提交链零阻塞 |
| UTS Promise 吞值坑 | ✗(需要你自己踩) | ✓ 边界已全部包装,开箱即用 |
| 失败留痕 | 多数静默失败 | ✓ onDiag 全程留痕,丢了数据可查 |
| 安卓 / iOS / Web | 部分支持 | ✗(明确不做,见下方边界) |
一句话:它们卖的是「能存」,我们卖的是「杀后台也还在」。
适合你,如果
- 你的 App 数据只存本地(记账、草稿、离线单据、聊天记录、收藏),丢一条都是客诉;
- 你的用户是鸿蒙设备,且断网使用是常态(这正是回退最凶的场景);
- 你试过 checkpoint / 事务 / 官方 backup,杀后台后数据仍然丢,查不出原因;
- 你想自己写 SQL,只要一个可靠的内核 + 防回退兜底,不需要 ORM。
四道防线(怎么做到的)
写事务 → z_dbseq 盖 seq(与数据同事务提交,回退时 seq 同步回退)
→ commit
→ 库外标记写同一 seq(uni storage,不在 SQLite 回退范围)← 提交链到此返回,零阻塞
→ 1.2s 防抖后台维护:官方 backup 副本 → 拷 filesDir 耐久副本
- rdb 同目录官方副本:
st.backup()的产物是另一个文件,不在系统「异常终止快照回退」作用范围; - filesDir 耐久副本:系统只回退 rdb 目录,filesDir(应用沙箱)不受影响——这是最终恢复源;
- seq 三方裁决:启动时比对 主库 seq / 副本 seq / 库外标记,判定「发生过回退且副本更新」→ 自动从副本 restore 主库,恢复结果二次校验;
- 完整性守卫:
st.backup疑似先建空文件、异步补写、Promise 提前 resolve(真机实证副本曾拷成 4096B 空库)——尺寸比对 + 原子拷贝(先 .tmp 后 rename)+ 500ms 延迟重试兜底。
所有失败路径(备份/checkpoint/裁决/恢复)通过 onDiag 回调留痕,绝不静默吞错。
快速上手(3 行起步)
import { createSafeSqlite } from '@/uni_modules/mqy-safesqlite/js/safe-sqlite'
// App onLaunch 初始化一次
const db = createSafeSqlite({
dbPath: 'app.db', // 库文件名
extKey: 'myapp_db_seq', // 库外标记的 storage 键(多库实例各用各的)
onDiag: (act, detail) => console.log('[safesqlite]', act, detail)
})
await db.open() // 打开 + 自动启动裁决(检测到回退自动恢复)
await db.tx([
'CREATE TABLE IF NOT EXISTS notes (id TEXT PRIMARY KEY, text TEXT)',
"INSERT OR REPLACE INTO notes VALUES ('n1', 'hello')"
])
const rows = await db.select('SELECT * FROM notes') // 对象数组
五分钟验证(不用信我,信你的设备)
await db.tx(["INSERT ..."])写几条数据;- 上滑划卡杀掉 App;重新打开 → 数据还在;
- 开飞行模式重复 1-2(最严格场景)→ 数据同样在;
onDiag看boot_seq:主库 / 副本 / 库外标记三方一致 = 机制在工作;- demo 目录有现成演示页(写一条 / 刷新 / 手动维护 / 诊断日志),拷进工程即可跑。
API
createSafeSqlite(options)
| 选项 | 默认 | 说明 |
|---|---|---|
dbPath |
'app.db' |
库文件名(relationalStore 只认文件名,落应用 database 目录) |
extKey |
'safesqlite_db_seq' |
库外提交标记的 uni storage 键,多库实例必须各用各的键 |
maintainDelay |
1200 |
提交后副本维护防抖毫秒(连续写入自动合并成一次) |
docRetryMax |
4 |
耐久副本不完整时的重试上限 |
onDiag |
null |
(act, detail) => void 诊断回调,建议接你的日志 |
core |
null |
极少数工程 @ 别名解析失败时,手动注入 UTS 内核 |
实例方法
| 方法 | 说明 |
|---|---|
open() |
打开库 + 启动裁决(幂等)。裁决任何异常都放行启动,不卡死 App |
tx(sqls: string[]) |
事务执行 SQL 数组:自动 begin/commit/rollback、盖 seq、写库外标记、调度副本维护 |
exec(sql) |
执行单条写 SQL(自动盖 seq,非事务) |
select(sql) |
查询,返回对象数组(INTEGER/REAL→number,TEXT→string,NULL→键缺失) |
maintain() |
手动触发一次 checkpoint + 双副本刷新(一般不需要,自动调度已覆盖) |
close() |
关闭库(自动取消未触发的维护定时器) |
stamp() |
读库外标记当前值(诊断用) |
顶层导出
| 导出 | 说明 |
|---|---|
isHarmonyBackend() |
当前是否鸿蒙环境(安卓/iOS 请分流到 plus.sqlite) |
VERSION |
插件版本 |
边界与局限(先说清楚,免得买错)
本插件不做的事,以及对应解法:
- 仅鸿蒙 App(app-harmony,API 12+)。安卓/iOS 没有回退问题,请继续用
plus.sqlite(本插件提供isHarmonyBackend()供分流);Web/小程序不支持。 - 不含 ORM / Schema Builder / 迁移 / 参数绑定。定位是「可靠内核 + 防回退安全层」,SQL 由你拼。拼接字符串值时请自行转义单引号(或用本插件只处理已知安全值);需要完整 ORM/迁移能力的请选结构化数据库插件。
- 不提供整库加密。敏感字段请在写入前由业务层加密(SQLCipher / HUKS 等方案与本插件不冲突)。
- 不含云端同步。防回退解决的是「本地不丢」;换机/多端请配合你自己的服务端同步。
- 极端窗口可能丢最后一两笔:提交后 1.2 秒内立即杀后台且副本未刷新时,恢复的是上一个副本快照。此时启动裁决会以
restore_partial诊断明确报告差了几笔,业务可据此向服务端补拉。把maintainDelay调小可收窄该窗口(代价是副本刷新更频繁)。 - uts 插件改动需重新打包:本插件含原生 UTS 代码,升级后需重新制作自定义基座/重新打包,不能只热更 WGT。
- 验证设备说明:真机验证基于 HarmonyOS 真机(API 18+ SDK)。不同 SDK 版本的 relationalStore 行为可能有差异,收费插件均支持免费试用,建议先试用跑完上面的五分钟验证再购买。
- 隐私:不申请系统权限、不采集数据、无广告,数据全部留在应用沙盒内。
FAQ
Q:我用了官方 backup,为什么数据还是丢? A:因为官方 backup 的副本与主库同在 rdb 目录,系统回退是整目录回退,副本一起退。必须把副本拷到回退范围之外(filesDir),并在启动时按标记裁决——这正是本插件做的事。
Q:PRAGMA wal_checkpoint(TRUNCATE) 不是能防丢吗? A:真机取证证明它对抗不了服务层整目录回退(checkpoint 后杀后台照样回退)。本插件仍保留 checkpoint(让主文件尽量持全量),但它只是辅助,不是防线。
Q:为什么我直接 import UTS 内核后,链式 .then 拿到的值是 undefined?
A:UTS Promise 的 .then 回调会触发,但链式返回值被吞成 undefined(真机实证的通用坑)。必须先在边界包成原生 JS Promise(jsWrap,实现见 js/safe-sqlite.js)。用本插件 JS 层则无需关心。
Q:多库实例怎么配?
A:每个库一个 createSafeSqlite 实例,dbPath 与 extKey 各用各的,互不干扰。
Q:恢复语义是什么?会恢复成多旧的版本?
A:恢复源是「最后一次成功提交并完成副本刷新」的快照,正常滞后 ≤ 维护周期(默认 1.2s)。裁决与恢复结果都有诊断事件(restored / restore_partial / rollback_detected),丢了多少笔一查便知。
Q:能和服务器同步共存吗?
A:不但共存,而且互补——本插件保「本地不丢」,你的同步保「换机/多端一致」。rollback_detected(回退但副本同旧)事件就是「本地与副本都救不回」的明确信号,此时向服务端补拉即可。
关键词
uni-app sqlite / uni-app x sqlite / HarmonyOS sqlite / 鸿蒙数据库 / relationalStore / 鸿蒙本地存储 / 杀后台数据丢失 / 数据库防回退 / SQLite backup / 本地数据库 / 离线存储 / UTS 插件

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 0
赞赏 0
下载 12744888
赞赏 1959
赞赏
京公网安备:11010802035340号