做小程序这几年,被问得最多的问题之一是「用户数据能不能帮我找回来」。说实话,每次看到这种消息我都替对方捏把汗——大部分时候答案是找不回来。不是技术做不到,而是压根没做过备份。今天就把我踩过的坑和现在在用的方案整理一下,能救一个是一个。

先搞清楚:你要备份的到底是什么
很多人一说备份就想到数据库,其实小程序里的数据来源挺杂的,得分开看:
- 云开发数据库:订单、用户资料、表单提交,这是重灾区,也是最该备份的。
- 云存储文件:用户上传的图片、合同、录音,丢了基本没法重建。
- 本地缓存 Storage:看起来不重要,但有些小工具类小程序把草稿存本地,用户换手机就全没了。
- 后台管理端数据:如果你的运营后台是自建的,那部分数据往往被忽略。
先列清单,再谈方案。我见过有人吭哧吭哧写了导出脚本,结果只导了 users 表,订单表一个字没动,这备份等于没做。
云开发自带的备份能力,够不够用
微信云开发控制台里确实有数据库回档功能,付费版可以按天回档。但它有几个限制你得知道:
- 回档是整库回档,不能只恢复某一张表或某几条记录,误删一条数据想单独捞回来很难受。
- 保留周期有限,超过时间窗口就没了。
- 云存储文件不在这套机制里,得单独处理。
所以我的结论是:自带的回档当兜底,但不能当唯一方案。真正靠谱的是自己定时把数据导出来,落到你能完全掌控的地方。
我现在的做法:定时云函数导出
思路很简单,写一个云函数,每天凌晨跑一次,把核心集合分批查出来,转成 JSON 或 CSV 传到云存储的备份目录,同时把备份文件的元信息记一张表。核心代码大概长这样:
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
const db = cloud.database()
const COLLECTIONS = ['orders', 'users', 'forms']
const PAGE = 100
exports.main = async () => {
const date = new Date().toISOString().slice(0, 10)
for (const name of COLLECTIONS) {
let all = []
let skip = 0
while (true) {
const res = await db.collection(name)
.skip(skip).limit(PAGE).get()
all = all.concat(res.data)
if (res.data.length < PAGE) break
skip += PAGE
}
const content = JSON.stringify(all)
const up = await cloud.uploadFile({
cloudPath: `backup/${date}/${name}.json`,
fileContent: Buffer.from(content)
})
await db.collection('backup_log').add({
data: { name, date, count: all.length, fileID: up.fileID }
})
}
return 'done'
}
几个细节值得说:
- 分页一定要做,云函数单次查询有条数上限,数据量大的集合不分页直接查会漏。
- 云函数执行时间有上限,集合特别多的时候拆成多个函数,或者用云调用触发串行跑。
- 备份文件别只放云存储,云存储本身也是同一个账号下的,账号出问题一起没。我会再加一步,把文件同步到对象存储或者自己的服务器。
备份做完了,恢复才是真考验
这是最容易被跳过的一步。备份文件躺在那里不代表你能用。我建议至少每季度做一次恢复演练:拿一份备份,导入到一个测试环境,看看字段对不对、关联关系还在不在、时间格式有没有被转坏。
没验证过的备份,只能叫「数据副本」,不能叫「备份」。
恢复的时候注意两点:一是导入前先清空目标集合,否则会重复;二是批量写入用云函数分批 add,一次塞几千条会超时。
本地缓存那点事
如果你的小程序有草稿、收藏这类本地数据,给用户留个出口。最简单的做法是在设置页加一个「导出我的数据」,把 Storage 里的关键 key 拼成 JSON,让用户复制或者发到邮箱。成本很低,但用户感知很强,尤其是工具类小程序。
最后几句实在话
数据备份这事,做的时候没人夸你,出事的时候能救命。别追求一步到位搞个多活架构,先把「每天自动导出一份、异地存一份、每季度恢复一次」这三件事落地,就已经比大多数小程序强了。真要出问题,你手里有东西,心里就不慌。

评论 (0)
还没有评论,快来抢沙发吧~