更新记录

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.jsprocessNDJSON 函数里的过滤逻辑:

// 默认:按 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.jsonsuccess.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 自动生成,排查失败记录

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

暂无用户评论。