更新记录
1.0.0(2026-07-08)
v1.0.0(2026-07-08)
【首发】通用数据批量迁移工具
✨ 功能:
- 支持大文件流式逐行处理,内存占用恒定
- 白名单条件筛选,只迁移指定数据
- 断点续传,中断后重启自动跳过已完成记录
- 失败自动重试(指数退避),最多重试 3 次
- 试跑模式,先发 1 条验证链路再全量跑
- 实时进度条,显示速率、成功/失败数、预计剩余时间
- 自动清洗 MongoDB $oid 扩展格式为普通字符串
- 支持本地运行和 uniCloud 云函数两种模式
- 零依赖,仅使用 Node.js 内置模块
平台兼容性
云端兼容性
| 阿里云 | 腾讯云 | 支付宝云 |
|---|---|---|
| √ | √ | √ |
云函数类插件通用教程
使用云函数类插件的前提是:使用HBuilderX 2.9+
为防止白嫖, 核心代码隐藏了, 购买后请联系作者获取完整代码
qianyi — 通用数据迁移脚本
从 NDJSON 文件中逐行流式读取数据,按条件筛选,逐条 POST 到目标接口。
特性:断点续传 · 失败重试 · 实时进度 · 试跑模式 · MongoDB $oid 自动清洗
适用场景
只要满足以下条件,都可以用它完成数据迁移,不限行业、不限数据表结构:
你有一个"源"
| 形态 | 说明 |
|---|---|
| uniCloud 数据库导出 | 从 uniCloud DB 或阿里云 MongoDB 导出的 JSON / NDJSON 文件 |
| 其他数据库导出 | MySQL、PostgreSQL、SQLite 等导出为 JSON Lines 格式 |
| 第三方系统数据 | 任何能导出为 NDJSON(一行一条 JSON)的数据源 |
| CSV / Excel 转换后 | 先转为 NDJSON,即可批量导入 |
你有一个"目标"
| 形态 | 说明 |
|---|---|
| uniCloud 云函数 + 数据库 | 数据写入 uniCloud 的 DB 集合,脚本已附带目标云函数模板 |
| 任意 HTTP 接口 | 只要接受 POST JSON,任何语言/框架/云平台都可以 |
| 自建服务 | 自己的后端 API,接收数据后做业务处理再入库 |
| 其他云服务 | 腾讯云、阿里云函数计算、Vercel、Supabase 等 |
典型使用场景
| 场景 | 说明 |
|---|---|
| 🔄 多小程序用户合并 | 多个抖音/微信小程序的用户数据,按 app_id 白名单筛选迁移到一个库 |
| 📊 历史数据归档 | 将老旧系统的海量数据批量导入新系统 |
| 🏗️ 系统迁移/重建 | 换框架、换数据库、换云平台时批量搬数据 |
| 🔀 跨平台数据同步 | 从 A 平台导出,筛选后同步到 B 平台 |
| 🧹 数据清洗后迁移 | 结合自定义筛选逻辑,只迁移符合条件的数据(如排除虚拟用户) |
| 🚀 批量 API 导入 | 如果目标系统提供 REST API 批量写入,直接用本脚本推送 |
| 📦 SaaS 数据导入 | 为客户做数据初始化,把他们的老数据导入你的 SaaS 系统 |
核心功能
| 功能 | 说明 |
|---|---|
| 📁 2GB+ 大文件无压力 | 流式逐行读取,内存恒定,不会 OOM |
| 🔁 断点续传不丢数据 | 中断后重跑自动跳过已完成记录 |
| 🧪 试跑模式先验证 | --test 只发 1 条,确认链路通了再全量跑 |
| 🎯 灵活筛选 | 白名单 + 自定义条件,只迁移你想要的数据 |
| 📊 实时进度条 | 速率、ETA、成功/失败计数一目了然 |
| 🔧 零依赖 | 只用 Node.js 内置模块,不需要 npm install |
| ☁️ 可部署为云函数 | 支持 uniCloud 云函数模式,分批执行防超时 |
目录结构
qianyi/
├── index.js # 迁移主程序
├── package.json # uniCloud 云函数配置(部署到 uniCloud 时需要)
├── data.json # ← 你的 NDJSON 数据文件(需自行放入)
├── success.log # 自动生成:已迁移成功的 _id 列表(断点续传依据)
├── failed.log # 自动生成:迁移失败的 _id + 错误原因
└── README.md # 本文档
快速开始
1. 准备数据
将数据文件命名为 data.json,放入本目录。必须是 NDJSON 格式(每行一条完整的 JSON 记录):
{"_id":"xxx01","app_id":"tt2d975b2ab2","userId":"u001","event_time":1234567890,...}
{"_id":"xxx02","app_id":"tt2d975b2ab2","userId":"u002","event_time":1234567891,...}
注意:
- 如果源数据是 JSON 数组
[{...},{...}],需先转为 NDJSON- 如果
_id是 MongoDB 格式{"$oid":"xxx"},脚本会自动转为字符串"xxx"- 不同数据表的字段名各不相同,请根据实际情况配置下方的
FILTER_FIELD
2. 配置 index.js
打开 index.js,修改顶部配置区:
// 【必填】目标迁移接口 URL
const TARGET_URL = 'https://your-target-url.com/migrate';
// 【必填】用于筛选的字段名(根据你的数据表结构调整)
// 比如你的数据表里用的是 app_id,就填 'app_id';用的是 appid,就填 'appid'
const FILTER_FIELD = 'your_filter_field';
// 【必填】筛选字段的允许值集合(白名单)
// 只有 FILTER_FIELD 的值在此集合中的记录才会被迁移
const FILTER_VALUES = new Set([
// 'value1',
// 'value2',
]);
// ---- 可选调优 ----
const CONCURRENCY = 10; // 并发请求数(建议 5~20)
const MAX_RETRIES = 3; // 失败重试次数
const REQUEST_TIMEOUT = 30000; // 请求超时(毫秒)
3. 试跑验证
node index.js --test
只迁移 1 条,验证配置是否正确:
🧪 试跑模式:只迁移 1 条,验证链路
🔍 正在查找第一条匹配筛选条件的记录...
✅ 找到匹配记录:
_id: 68904e2a189f86d5e1630d1e
your_filter_field: tt2d975b2ab2
总字段数: 19
所有字段: _id, userId, event_time, app_id, status, ...
📤 正在发送 POST 请求...
✅ 迁移成功!
HTTP 状态: 200
耗时: 185ms
🎉 试跑通过!可以正式迁移了
4. 全量迁移
# 交互确认模式
node index.js
# 跳过确认,直接执行
node index.js --run
# 指定数据文件
node index.js --run /path/to/your-data.json
实时进度
[████████░░░░░░░░░░░░] 42% 已扫描:600,000 匹配:86,797 ✓成功:62,207 ✗失败:59 ↷跳过:0 进行中:10 速率:68/秒 ETA:8分32秒
| 指标 | 含义 |
|---|---|
| 进度条 + 百分比 | 基于预扫描匹配总数的完成比例 |
| 已扫描 | 已读取的 NDJSON 行数 |
| 匹配 | 命中筛选条件的条数 |
| ✓成功 | 迁移成功的条数 |
| ✗失败 | 重试 3 次仍失败的条数 |
| ↷跳过 | 断点续传跳过的条数(重新执行时出现) |
| 进行中 | 当前并发在途请求数 |
| 速率 | 每秒成功迁移条数 |
| ETA | 预计剩余时间 |
断点续传
每迁移成功一条,立即将 _id 追加写入 success.log。中断后重新运行会自动跳过已完成记录。
| 上次状态 | 重新执行后 |
|---|---|
| 迁移成功的 | ✅ 跳过 |
| 迁移失败的 | 🔄 重新尝试 |
| 还没扫到的 | 🆕 正常迁移 |
# Ctrl+C 中断后直接重新运行即可
node index.js --run
只要
success.log不丢失,断点续传就有效。
自定义筛选条件
如果白名单匹配不够用,可以修改 index.js 中 processNDJSON 函数里的过滤逻辑:
// 默认:按 FILTER_FIELD + FILTER_VALUES 白名单
if (!FILTER_VALUES.has(record[FILTER_FIELD])) return;
// 示例:增加额外条件,只迁移 status=0 的记录
if (!FILTER_VALUES.has(record[FILTER_FIELD])) return;
if (record.status !== 0) return;
// 示例:排除 nickname 以"虚拟人"开头的
if (!FILTER_VALUES.has(record[FILTER_FIELD])) return;
if (/^虚拟人/.test(record.nickname)) return;
目标接口约定
迁移脚本将每条记录的全部字段通过 POST JSON 发送到目标接口。目标接口应返回:
{
"code": 0, // 0=成功,非0=失败
"message": "插入成功",
"data": {}
}
脚本会双重校验:HTTP 状态码(200)+ 业务 code(0)。
目标云函数模板(uniCloud)
'use strict';
const db = uniCloud.database();
exports.main = async (event, context) => {
let data = {};
try {
data = JSON.parse(event.body);
} catch (e) {
return { code: -1, message: '请求体不是合法 JSON', error: e.message };
}
try {
// 将 'your_collection_name' 替换为你的目标集合名
const res = await db.collection("your_collection_name").add(data);
return { code: 0, message: '插入成功', data: res };
} catch (e) {
// ---- 处理 _id 重复(可选) ----
const _id = data._id;
if (_id) {
const exist = await db.collection("your_collection_name")
.where({ _id }).get();
if (exist.data && exist.data[0]) {
// 存在相同 _id,剔除后让数据库自动生成新 id
delete data._id;
try {
const res = await db.collection("your_collection_name").add(data);
return { code: 0, message: '插入成功(_id 重复,已剔除后重新插入)', data: res };
} catch (reErr) {
return { code: -2, message: '剔除 _id 后仍插入失败', error: reErr.message };
}
}
}
return { code: -2, message: '数据库插入失败', error: e.message };
}
};
常见问题
Q: 预扫描匹配到 0 条怎么办?
检查 FILTER_FIELD 是否和数据中的字段名一致。先跑 node index.js --test 会打印出数据中所有字段名。
Q: 数据文件太大(2GB+),会不会内存溢出?
不会。使用 readline 流式逐行读取,内存只保留当前行(约几 KB)。
Q: 怎么知道有没有漏迁移的数据?
对比 data.json 和 success.log,存在于前者但不在后者中的 _id 即为未迁移数据。
Q: 并发数设多少合适?
- 目标接口性能好:20~30
- 一般情况:10
- 容易限流:5
Q: 如何部署为云函数?
部署到 uniCloud 后通过 HTTP 触发器调用:
{
"batchSize": 5000,
"ndjsonPath": "/tmp/data.json",
"totalMatched": 86600
}
返回 hasMore: true 表示还有数据待处理。
文件说明
| 文件 | 说明 |
|---|---|
index.js |
主程序,配置在顶部 |
package.json |
uniCloud 部署配置(仅云函数部署时需要) |
data.json |
自行放入的 NDJSON 数据 |
success.log |
自动生成,断点续传依据 |
failed.log |
自动生成,排查失败记录 |

收藏人数:
购买普通授权版(
导入插件并试用
赞赏(0)
下载 8
赞赏 0
下载 34741
赞赏 158
赞赏
京公网安备:11010802035340号