v2 → v3 离线迁移
使用 wkcli migrate 从未修改的 v2 停机备份生成全新 v3 集群,完成数据校验、客户端切换与回退准备。
wkcli migrate 将原版 v2 的停机数据转换为原生 v3 数据目录。不需要修改或升级已部署的 v2:prepare 在迁移机上离线读取备份,不是需要安装到 v2 的接口。
API 和 SDK 接口兼容不代表磁盘格式兼容。不要让 v3 直接打开 v2 数据目录,也不要用普通滚动升级替代迁移。
适用范围
| 项目 | 要求 |
|---|---|
| 来源版本 | 原版 v2.2.5-20260422,提交 a888f89533d0e7d1b2030e06504ca97f1ad891d4;自定义修改需另行确认 |
| 迁移方式 | 全部来源停写、停机后离线迁移;不支持在线增量追赶 |
| 来源与目标 | 支持单节点集群和多节点集群,可以改变节点数;目标必须是全新空集群 |
| 运行平台 | 源文件锁支持 Linux、macOS;插件程序还须与目标系统和架构匹配 |
| 集群布局 | 固定 256 个物理哈希槽,逻辑 Slot 数和副本数由目标计划指定 |
| 工具与目标版本 | 固定经过验证的同一 v3 源码提交构建迁移工具和服务端,或使用配套且已核对摘要的交付包 |
迁移机需要同时容纳完整冷备、工作空间、归档、全部目标副本和验收前快照。先用代表性数据演练并记录峰值空间与耗时;100 GiB/4 小时尚未完成性能验收,不能作为停机时长承诺。
1. 保存冷备并准备工具
原 v2 仍在运行时,先停止全部业务写入口,等待日志应用、拓扑变更和通知队列排空,再正常停止所有节点并关闭自动拉起。保存各节点完整数据目录、原程序、配置及环境变量。不要复制仍在写入的数据库目录。
如果已经有完整停机备份,可以直接使用,不必为运行 prepare 重启 v2。收集所有来源节点;工具会核对文件锁、扫描前后的文件清单和摘要。通知队列非空会阻止完成;队列为空也不能证明外部系统已经收到全部旧通知。
冷备与迁移归档都要保留
迁移归档保存原始业务行、索引和迁移证据,不包含全部原始 WAL 文件字节,不能替代完整文件系统冷备。回退依赖原 v2 冷备。凭据、消息正文和插件配置都属于私有数据。
在已固定提交的 v3 源码目录中构建,所需 Go 版本以该提交的 go.mod 为准:
umask 077
mkdir -p /srv/tools /srv/wkmigrate/reports /srv/wkmigrate/work /srv/wkmigrate/targets
GOWORK=off go build -o /srv/tools/wkcli ./cmd/wkcli
GOWORK=off go build -o /srv/tools/wukongim-v3 ./cmd/wukongim
git rev-parse HEAD > /srv/wkmigrate/reports/tool-source.txt
sha256sum /srv/tools/wkcli /srv/tools/wukongim-v3 > /srv/wkmigrate/reports/binaries.sha256
/srv/tools/wkcli migrate --helpmacOS 可用 shasum -a 256 代替 sha256sum。构建应面向实际运行平台。使用交付包时,先与独立交付记录比对压缩包摘要,再校验包内文件;从 v3.0.0-beta.12 起,官方归档和原生安装包均提供两个程序,获取方式见 wkcli 安装。迁移前比较 wkcli version --output json 与 wukongim version --output json,确认版本、提交和构建来源一致。
2. 固定迁移计划
把下面的三节点示例保存为 /srv/wkmigrate/plan.json,替换节点、路径、地址、创建时间和实际源分片数。source_commit 是你提供的来源版本证据,工具不会据此自动鉴定旧二进制。
{
"version": 1,
"source_commit": "a888f89533d0e7d1b2030e06504ca97f1ad891d4",
"sources": [
{"node_id": 1001, "data_dir": "/srv/v2-snapshots/node1001/data", "shard_count": 8},
{"node_id": 1002, "data_dir": "/srv/v2-snapshots/node1002/data", "shard_count": 8},
{"node_id": 1003, "data_dir": "/srv/v2-snapshots/node1003/data", "shard_count": 8}
],
"target": {
"cluster_id": "migration-v3-new",
"created_at": "2026-09-09T00:00:00Z",
"slot_count": 256,
"hash_slot_count": 256,
"replicas": 3,
"channel_replicas": 3,
"nodes": [
{"node_id": 101, "addr": "10.20.0.11:7000", "data_dir": "/srv/wkmigrate/targets/node101"},
{"node_id": 102, "addr": "10.20.0.12:7000", "data_dir": "/srv/wkmigrate/targets/node102"},
{"node_id": 103, "addr": "10.20.0.13:7000", "data_dir": "/srv/wkmigrate/targets/node103"}
]
}
}sources[].data_dir指向每个来源节点的完整业务数据库目录;shard_count填原部署的实际业务 DB 分片数。- 单节点集群只列一个目标节点,两类副本数均为 1。多节点时,副本数不能超过目标节点数;目标节点 ID 为 1–1023。
- 使用绝对路径,源目录、工作空间、归档和各目标目录互不包含。只创建父目录,新的工作空间及目标
data_dir本身必须不存在;预建空目录会因缺少迁移身份而被拒绝。 - 计划一旦用于迁移,重试保持原样。改变来源、目标、策略或工具版本时,保留旧现场,使用新的工作空间、归档和全新目标重新执行。
先决定哪些数据迁入
默认行为严格检查业务等价性,下面的处理必须显式选择,不能把某次演练的例外直接套用到另一份备份。
| 数据 | 处理原则 |
|---|---|
| 普通消息 | 保留未排除消息的 MessageID、ClientMsgNo、正文和原生 RedDot;不兼容字段会阻止完成 |
| 用户、设备与权限 | 迁入原凭据、成员和权限;重复设备凭据不适用“消息保留最新”规则 |
| 会话与读位置 | 保留可等价表达的状态;重新编号时同步映射已读和删除位置 |
| 旧管理数据 | 可归档的记录保存在带校验和的源归档中,报告逐项列出 |
| 插件与外部集成 | 需要明确的程序、配置和兼容映射;留档或关闭插件不等于业务兼容 |
如果业务方决定不迁入 CMD、整条流消息,并按规则去重和重新编号,将以下字段合并到计划顶层:
{
"messages": {
"keep_latest_duplicates": true,
"exclude_cmd": true,
"exclude_streams": true,
"compact_sequences": true
},
"exclusions": {"legacy_stream_storage": true}
}legacy_stream_storage 仅排除旧 Stream/StreamMeta 两张表;exclude_streams 才排除消息表中的流主消息及其明确关联的事件投影和游标。与保留消息共享事件身份时仍会阻断。exclude_cmd 同时省略旧 CMD 会话和同步位置。被排除的源行完整留在归档中。
“最新”按同一频道的原 MessageSeq 判断:MessageID 重复,或频道相同且非空发送者与非空 ClientMsgNo 均相同,保留较新记录。发送者或 ClientMsgNo 为空时不参与 ClientMsgNo 去重;MessageID 去重独立执行。跨频道 MessageID 冲突或相互矛盾的保留选择仍会阻断。不能按节点数累加物理副本,得到业务删除数量。
重新编号后,保留消息按原顺序成为 1…N,新消息从 N+1 继续。已读和删除位置映射为旧位置之前(含该位置)仍保留的消息数量。全排除频道尾部为 0,后续从 1 开始;不会创建占位消息或更改 v3 存储格式来补空号。原历史缺洞仍会阻断。
3. diagnose 与 prepare
先诊断冷备,使用单独的诊断工作空间:
/srv/tools/wkcli migrate diagnose --plan /srv/wkmigrate/plan.json \
--workspace /srv/wkmigrate/work/diagnose \
> /srv/wkmigrate/reports/diagnose.json退出码 1 可能表示业务阻塞或扫描不完整,先阅读报告及其完整明细。诊断成功不代表已选定权威副本,也不会生成可导入的目标。不能把诊断工作空间用于准备阶段。
解决阻塞并确定计划后执行:
/srv/tools/wkcli migrate prepare --plan /srv/wkmigrate/plan.json \
--workspace /srv/wkmigrate/work/prepare \
> /srv/wkmigrate/reports/prepare.jsonprepare 会检查原始格式、索引和分片,结合持久化配置、Slot 日志及应用位置确定来源,比较正式副本,并生成转换清单。副本不一致时不会自行挑选最长 follower。此步骤不修改原 v2,也不启动或创建目标集群。
成功要求退出码 0、status: "prepared"。保存捕获和选择摘要、迁入/排除计数,以及启用序号转换时的 sequence_mapping 文件、行数和 SHA-256。源权威、重复凭据、缺失会话或插件等阻塞应逐项处理;专项命令 authority、dedupe-plan 和捕获绑定决策详见工程操作手册。不要删除原行、清空字段或改写证明来通过检查。
4. export:封存原始归档
/srv/tools/wkcli migrate export --plan /srv/wkmigrate/plan.json \
--workspace /srv/wkmigrate/work/prepare \
--archive /srv/wkmigrate/source-archive \
> /srv/wkmigrate/reports/export.json沿用成功 prepare 的工作空间。导出再次检查源未变,生成分块归档、清单和 COMPLETE 标记。缺块、校验失败或缺少完成标记时不能导入。保存归档和序号映射后,可卸载迁移机上的源副本;完整原 v2 冷备继续保留。
5. import 与 verify:目标保持停机
导入和独立校验仅依赖计划及完整归档。下面分别使用全新的工作空间,使校验不依赖导入过程的临时结果:
/srv/tools/wkcli migrate import --plan /srv/wkmigrate/plan.json \
--workspace /srv/wkmigrate/work/import \
--archive /srv/wkmigrate/source-archive \
> /srv/wkmigrate/reports/import.json
/srv/tools/wkcli migrate verify --plan /srv/wkmigrate/plan.json \
--workspace /srv/wkmigrate/work/verify \
--archive /srv/wkmigrate/source-archive \
> /srv/wkmigrate/reports/verify.json第一版在迁移机上生成全部目标节点目录。导入成功为退出码 0、status: "imported";此时仍不要启动目标。verify 从归档中的原记录重新推导预期值,逐字段比较全部目标副本,包括凭据、权限、会话、消息、原生索引、提交边界和启动产物,不只比较数量。
| 校验结果 | 必须确认 |
|---|---|
| 退出码及状态 | 退出码 0,status: "offline_verified" |
| 来源 | selection_digest 与本次准备结果一致 |
| 拓扑与计数 | 节点数符合计划,verified_message_replicas 等于保留业务消息数 × 消息副本数 |
| 切换状态 | cutover_ready: false 是正常结果,表示还需要运行和客户端验收 |
首次启动前保存校验报告和完整目标快照。启动后的库不能再作为初始导入状态重新验证或覆盖导入。
中断后怎么处理
保留计划、归档、目标和原日志。目标从未启动、计划和归档不变时,可以重跑相同 import 命令;已完成部分会检查指纹,不会覆盖不同代的数据。未启动目标的 verify 可重新完整执行。另存每次日志,避免覆盖失败证据。无迁移身份的目录会被拒绝,不要删除身份或完成标记来强行重用。
若需要在隔离 Docker 环境复演整个离线流程,可使用仓库的演练脚本与示例。它提供只读源、独立工作空间、磁盘/时间保护及 --dry-run;封装脚本不会自动恢复已有输出目录,恢复前仍需按上述规则检查现场。
6. 启动 v3 并验收
将每个目标目录完整复制到对应主机,保持计划中的相同绝对数据路径和文件权限;不要只复制消息库,Controller 快照和迁移标记也必须保留。复制时所有目标保持停机。
准备各节点的 wukongim.toml,保持 node.id、node.data_dir、cluster.id、cluster.nodes、cluster.initial_slot_count、cluster.hash_slot_count、cluster.slot_replica_n 和 cluster.channel_replica_n 与计划一致。TLS、监听/公布地址、网关认证、Webhook 地址与网络保护、插件及业务后端配置须另行核对,数据库导入不会替你搬运或验证这些配置。原生 Webhook 不提供签名;若业务使用外部签名代理,单独迁移并验证代理配置。参考集群配置和安全配置。
使用配套 v3 程序隔离启动全部目标节点,暂不接入生产流量。至少验证:
- 每个节点
/readyz就绪,Controller、各逻辑 Slot 和频道副本状态正常。 - 原 Token 与相同
device_flag登录成功,错误 Token 被拒绝;不要通过重新设置 Token 来“验证”旧凭据。 - 历史首尾和跨页内容、ID、序号、ClientMsgNo、RedDot 一致,权限、会话列表和已读状态符合计划。
- 新消息序号严格大于当前频道尾部,重试相同幂等键不重复,新增未读正确。单聊发送填写接收者 UID,不复用接收者视角历史查询中的对方 UID。
- 整组重启后历史、新消息及未读正确;多节点场景按已制定的方案验证单节点故障恢复。
- 保留的事件投影、插件、Webhook、推送和实际业务客户端通过各自验收。
写入测试使用隔离演练副本。API 检查不能代替 SDK 登录、真实界面和外部业务集成验收。聊天 Demo 支持使用已有 Token 验收,不应创建或覆盖原凭据。
7. 客户端切换与回退边界
重新编号会使旧消息缓存和旧序号游标失效。采用清缓存方案时,按迁移代次清除客户端历史消息副本和派生同步游标,保留登录凭据,再从 v3 同步会话与历史。清理前单独处理未发送消息和草稿,避免把待发送队列误删或重复发送。服务端导入不会自动清理业务 App 的数据库。
若客户端采用映射方案,必须处理完整 sequence_mapping:被排除行的 target_seq 为 0,旧游标映射应使用 boundary_seq,不能当成仍存在的消息。可用 export-map 从归档重建映射:
/srv/tools/wkcli migrate export-map --plan /srv/wkmigrate/plan.json \
--workspace /srv/wkmigrate/work/map \
--archive /srv/wkmigrate/source-archive全部报告、运行测试和客户端验收通过后,运维再切换路由并逐步恢复流量。禁止新旧两套同时接受同一业务写入。
| 阶段 | 回退方式 |
|---|---|
| v3 尚未接收新生产写入 | 关闭新集群入口,按已演练步骤恢复原 v2 冷备及路由;恢复客户端的对应迁移代次 |
| v3 已接收新生产写入 | 不能直接切回旧 v2,否则会丢失新增数据;保留新库,按已验证的 v3 修复或备份恢复方案处理 |
第一版没有 v3 → v2 反向增量迁移。回退演练应使用备份副本,不能破坏保留的原冷备。固定版本的完整迁移和重启测试记录见交付包验收报告;其功能结果不等于你的性能、插件或生产切换验收。