简介:dbswitch是一款面向数据迁移与同步场景的开源工具,适用于需要将源端数据库批量迁移至目的端的开发、运维及数据管理人员,重点解决异构库表结构转换、全量与增量数据同步等问题。资源包内含完整工程源码与构建脚本,共506个文件,以Java核心代码为主,辅以前端Vue/JavaScript页面、SQL脚本、XML配置及Shell/Docker部署文件,整体体积约99.09MB,目录结构清晰,便于按模块查阅。工具支持基于正则的表名/字段名映射、主键表增量变更同步(CDC)以及insert/copy两种写入方式,读者可借助源码深入理解迁移同步的实现细节,也可基于构建配置快速部署试用,用于自身项目的改造与扩展。目前该资源已有793人学习,适合具备一定数据库基础、希望掌握数据迁移工具原理并落地实践的工程师。
1. 从一次凌晨切换事故说起:dbswitch 在批量迁移里到底解决什么
做数据库迁移的人多半经历过这种夜:源端 MySQL 二十几张业务表,目标端换成了国产库,字段类型对不上,业务方要求第二天 9 点前必须看到全量数据。手工写脚本的同事熬到凌晨三点,最后发现视图没迁、自增主键撞了、增量窗口里漏了二十分钟数据。dbswitch 这类工具之所以在国产化迁移和数据库同步场景里被反复提起,就是因为它把「源端数据库向目的端数据库批量迁移同步」这件事拆成了结构转换、全量抽取、增量回放三条明确的流水线,而不是让你在黑匣子里一次次试错。它适合正在做数据库替换、双库并行、以及需要在短切换窗口内完成数据追平的 DBA 和数据平台工程师。本文会按全量与增量的选型逻辑、最小可用配置、增量落地姿势、踩坑记录这个顺序,把这套方案讲透。
2. 先分清全量和增量:dbswitch 的两种同步模式与选型逻辑
2.1 全量迁移的本质:结构转换先行,数据抽取殿后
dbswitch 处理全量迁移时,并不是简单地把源端数据倒出来再灌进目标端。它先做的一件关键事是结构转换:读取源端库表的元数据,把字段类型、约束、索引、自增属性翻译成目标端能识别的 DDL。这一步是它和数据导出导入工具拉开差距的地方。以最常见的 MySQL 迁往达梦、人大金仓这类国产库为例,varchar(255)可能要被映射成VARCHAR2(255 CHAR),datetime(6)要确认目标端是否支持微秒精度,unsigned int可能要改成INT并单独处理取值范围。盲目按源端 DDL 原样执行,大概率会在目标端报类型不合法或精度截断。
数据抽取阶段,常见做法是按表分批拉取。每一批数据插入目标端时,控制在一个合理的事务大小内,避免大事务把目标端的 redo 日志撑爆。我一般会把单批行数设在 2000 到 10000 之间,具体看字段宽度和目标端磁盘 IO。这里值得留意的是:全量迁移的成功标准不是「跑完了」,而是「结构逐项一致、行数一致、关键字段抽样一致」,所以后面专门有一节讲校验。
2.2 增量同步的机制:日志解析与位点续传
增量同步解决的是全量迁完后,源端继续产生的业务变更怎么持续流到目标端。它的核心不再是「拉数据」,而是识别变更。业界两条技术路线:基于update_time之类的增量字段轮询,以及基于数据库日志的 CDC 解析。dbswitch 的增量能力在常见做法里依赖源端日志解析,比如 MySQL 的 binlog,把源端事务变更解析成目标端可执行的写入操作。
为什么强调基于日志而不是轮询时间戳?因为业务表如果存在删除操作,或者某些更新语句没有刷新update_time,轮询方案会漏数据。基于日志则能拿到完整的事务语义,包括行级变更前镜像和后镜像。代价也很明确:源端必须开启对应日志,格式要选对,且日志保留时长要覆盖增量任务可能中断的窗口长度。同步机制上,增量任务需要记住自己消费到哪个位点,这样进程重启后才能从断点继续,而不是从头重放或跳过一段。位点存储是增量配置里最不能省的一环,后面说参数时会展开。
2.3 全量和增量为什么几乎总是混用:切换窗口的接力逻辑
真实的割接流程里,很少有一轮全量直接宣布上线。常见做法是三步走:第一天先跑全量,让目标库有一份基线数据;业务继续跑,源端不断产生新变更;到了切换夜,跑一轮增量把基线之后的数据追平,等同步延迟降到零附近,再停业务、做最后一小段增量、校验、切流量。这个「全量铺底、增量追平」的组合,决定了切换窗口是 5 分钟还是 5 小时。
如果你只用全量,切换夜要停业务等待所有数据搬完;如果你只用增量,没有基线数据,增量回放也无从谈起。dbswitch 的价值在于把这两种模式放在同一个配置模型里,全量任务跑完后再启动增量任务,两边衔接时不需要导出导入中间文件。衔接是否顺滑,重点看增量任务能否从全量结束的位点开始——很多工具在这一步上是断裂的,零点几秒的间隙都会造成数据空洞,而 dbswitch 这类工具在配置正确时可以做到位点连续。
2.4 选型边界:dbswitch 与 DataX、Flink CDC 的取舍
很多团队会问:我有 DataX,也有 Flink CDC,为什么还要看 dbswitch?我的判断是三者的适用场景并不重合。DataX 强在异构数据源之间稳定的大批量抽取,但它的结构同步能力很弱,表结构转换基本靠人工写 DDL,而且 DataX 本身不支持增量位点管理。Flink CDC 强在实时性和分布式吞吐,但链路重,需要维护 Flink 集群、状态后端和 checkpoint,对一个小团队来说过重。
dbswitch 站在两者中间:轻量、配置化、能处理结构转换和增量同步,适合百 GB 级别、库到库的批量迁移同步,尤其是国产化迁移这类异构数据库替换场景。如果你的数据量到了 TB 级且对实时性要求到秒级,Flink CDC 是合理选择;如果你只是想把数据抽到文件或数仓,DataX 更顺手。做选型时我习惯列一张表:结构转换、增量机制、断点续传、运维成本、集群依赖,双方一栏栏比对,基本不会有争议。
3. 用 dbswitch 跑通第一轮全量迁移:从最小配置到校验
3.1 环境准备:拿到发布包后先确认什么
开箱一个 Java 系的迁移同步工具,第一件事不是急着启动,而是检查环境和配置结构。常见做法是发布包里有启动脚本和配置文件,配置里包含源端连接、目标端连接、迁移模式三块。跑全量迁移前,我一般会确认三件事:目标端驱动是否已放入 lib 目录;源端连接账号是否有读取元数据和数据的权限;目标端账号是否有建表、插入、索引权限。权限缺失是迁移任务在中途翻车的最常见原因,而且报错往往很晚——可能跑了半小时才在某一张表上失败。
另外,源端和目标端的网络连通性千万不要只测 ICMP,要用数据库客户端各连一次。JDBC 连接串里的主机名、端口、服务名写错会导致启动即失败,而这种失败经常被误判为工具问题,实际上只是连接串里一个字符的事。环境准备好的标准是:用命令行能分别连上两端数据库,执行select 1成功。
3.2 最小配置:以 YAML 形式描述源端与目标端
dbswitch 的配置在常见发布包里是 YAML 结构,核心是 source、target、migration 三段。第一轮建议只迁少数几张表,把配置风险压到最低。
source: type: mysql host: 192.168.10.15 port: 3306 username: migrate_user password: "ChangeMe@2024" database: shop target: type: dm host: 192.168.10.16 port: 5236 username: SYSDBA password: "ChangeMe@2024" database: SHOP migration: mode: full include_tables: - user - order - item batch_size: 5000 truncate_target: true这段配置的逻辑是:先指定源端库类型、地址和账号,再指定目标端库类型和账号,最后声明迁移模式为full,并把范围限制在 user、order、item 三张表。truncate_target: true表示每次重跑前先清空目标端表,保证任务幂等——全量迁移反复试跑时,不清空会导致行数翻倍。
参数说明里,batch_size决定每批插入目标端的行数,值太大容易造成目标端锁竞争和日志膨胀,值太小则吞吐上不去,5000 是一个稳妥起点;include_tables是白名单,防止把日志表、临时表也迁过去;mode: full切到incremental就是增量模式,全量增量共用这套配置骨架。
3.3 启动迁移任务并读日志
配置写好后,一般通过启动脚本加载配置文件运行。Java 系工具最常见的是sh bin/dbswitch.sh --config这种风格,日志会打到控制台和文件。
# 启动全量迁移任务,建议后台运行并把日志落盘 nohup sh bin/dbswitch.sh --config config/application.yml \ > logs/full_migration_$(date +%Y%m%d_%H%M%S).log 2>&1 & # 实时观察迁移进度 tail -f logs/full_migration_*.log启动后重点关注日志里的表级状态:结构转换是否成功、抽取多少行、插入多少行、每张表耗时多久。我习惯在日志里 grep 关键字ERROR和RETRY,如果某张表反复重试,基本可以断定目标端类型映射或约束有问题,不要干等,直接打断去看那张表的明细错误。全量迁移遇到单表失败时,常见做法是先把这张表从include_tables里摘掉,让整体任务先完成,再单独排查问题表。
命令里nohup和2>&1的作用是让任务脱离终端会话,避免你关掉 SSH 窗口时任务被杀掉;日志文件名带时间戳,方便和任务执行时刻对应排查。
3.4 结果校验:行数、主键、抽样三个层面
迁移跑完那一刻,永远先假设它有问题。第一层校验是行数,对每张表分别执行源端和目标端的count(*)并比对;第二层校验是主键最大值和最小值,这能暴露行数相同但内容错位的情况;第三层是抽样逐字段比对,随机抽 3 到 5 条数据,比对关键业务字段值,尤其是字符串、日期、金额这三类最容易出问题的类型。
-- 源端执行 SELECT COUNT(*), MAX(id), MIN(id) FROM shop.`order`; -- 目的端执行 SELECT COUNT(*), MAX(id), MIN(id) FROM SHAP.ORDER_T;这两条 SQL 结果的差异能直接定位问题方向:行数不一致说明有漏迁或重复迁移;行数一致但 max(id) 不同说明主键序列出了问题;两者都一致不等于逐行一致,所以要保留抽样比对这一步。校验脚本不要临时写,建议沉淀成一份固定 SQL 集,每次迁移都跑同一套,这样前后对比才有意义。
4. 增量同步的落地姿势:日志开启、位点管理与追平
4.1 增量同步的硬前提:源端必须开日志且保留够久
增量同步最常被人忽略的前提是源端数据库配置。MySQL 要开 binlog,并且格式建议设为ROW,因为STATEMENT格式对函数和存储过程的同步可能产生不一致。同时要设置binlog_expire_logs_seconds或对应的过期参数,保留时长至少要覆盖增量任务可能中断的最长时间。一个血泪经验:源端只开了 binlog 但保留半天,周末增量任务挂了没被发现,周一来看日志早就清了,位点断裂,只能重新全量。
在国产化迁移场景里还有一个高频问题:目标端是达梦或其他库,增量同步要借助源端日志解析,但源端如果是 Oracle 或 PostgreSQL,日志机制完全不同。Oracle 需要开启归档和补充日志,PostgreSQL 需要设置wal_level=logical。动手前先确认源端数据库的日志能力,再决定增量方案是否可行,这比调任何参数都重要。
4.2 增量任务的关键参数与位点存储
增量模式的配置继承全量的 source 和 target 段,但 migration 段要换成增量参数。常见配置长这样:
migration: mode: incremental incremental: server_id: 1002 start_position: "" checkpoint: storage: database table_name: dbswitch_checkpoint flush_interval_sec: 5逻辑说明:server_id在 binlog 订阅场景里相当于给源端注册一个从库身份,同一套源库里不能有两个任务用相同的 server_id,否则源端会互相踢掉会话;start_position留空表示从当前最新位点开始,如果要做增量追平,填上次记录的位置即可;checkpoint段决定消费位点存在哪里,强烈建议存到数据库表而不是本地文件,因为本地文件在任务被调度系统迁移或容器重建时极易丢失。
flush_interval_sec是位点刷新的间隔,5 秒代表最多在异常退出时重复消费 5 秒内的数据。这里需要理解一个取舍:拉长刷新间隔能降低数据库写入频率,但断电时可能重复消费更多数据;缩短到 1 秒则接近实时,但要承担更高的位点表写入开销。增量同步本质上是至少一次语义,所以目标端写入能力要支持幂等,或者接受少量重复后靠主键或去重逻辑兜底。
4.3 观察同步延迟并确认切换窗口
增量任务跑起来后,最需要盯的指标是同步延迟:源端当前日志位点与目标端已应用位点的差值。常见查看方式是看任务日志里周期性输出的位点信息,或者直接查 checkpoint 表里记录的位点与源端最新位点的差距。
当延迟逐步收敛到 5 秒以内,就可以考虑进入切换窗口了。切换窗口内的操作顺序我一般固定为:停掉源端写入口或只读,等待增量任务把最后一批变更消费完,确认延迟归零,再核对一遍行数和抽样数据,最后把流量切到目标端。这里有个细节:不要看到延迟归零立刻切,先等一到两个flush_interval周期,确保位点刷新落盘,否则任务一停,重启后可能从更早位置重放,切过去才发现重复数据。
增量阶段另一个常见瓶颈是目标端写入性能。源端业务高峰期的 TPS 如果高于目标端最大写入能力,延迟只会上涨不会收敛。遇到这种情况,优先检查目标端是否有锁等待、索引维护开销和归档日志空间,再考虑调大批量提交的线程数。增量任务的性能指标不是「跑得快」,而是「追得平」。
4.4 增量与全量的衔接:一个容易忽略的边界
很多第一次用的人会把全量任务和增量任务分开跑,这没问题,但要注意一个边界:全量任务运行期间源端产生的变更是否被增量任务覆盖。常见做法是让增量任务在全量任务启动前就记录一个起始位点,全量跑完后,增量从该位点开始追。如果全量跑完才启动增量并且从最新位点开始,中间几个小时的变更就丢了,业务方在切换后才可能发现数据是空的或缺失的。
我习惯在跑全量之前先记录源端当前位点,并把这个位点保存成文件或写进数据库。全量结束后,把增量任务的start_position指到这个保存的位点,让增量任务补追全量期间的数据。这样即使全量跑了十几个小时,切换夜的最终数据也是完整的。两条任务一前一后接力,而不是并行空跑。
5. 避坑指南:dbswitch 迁移同步最容易踩的 5 个现场
5.1 连接与配置类的坑:连接串、权限与字符集
现象一:任务启动后立刻报Communications link failure或Connection refused,但用命令行能连上库。原因是 JDBC 连接串和命令行客户端用的端口、服务名不一致。很多国产数据库的默认端口和 MySQL 不同,例如达梦默认是 5236,PostgreSQL 是 5432,连接串里写错端口是最低级的翻车。
解决:不要复制粘贴,逐字符核对端口、数据库名、用户名三项;先在一个简单的测试类或用数据库自带客户端验证该连接串可用,再填入配置。
现象二:全量迁移跑完,中文全部变成问号或乱码。原因是目标端表字符集或连接字符集不匹配。源端是utf8mb4,目标端默认字符集如果是GBK或没显式指定,字符串在转换时被截断。解决:在连接串里显式指定两端字符集,例如 MySQL 加?useUnicode=true&characterEncoding=utf8,迁移后立刻抽查含中文的字段,不要等全部跑完。
5.2 数据与性能类的坑:大表、主键与精度
现象三:单表几亿行,调大batch_size到 20000 反而越来越慢,目标端日志疯狂告警。原因是单批数据量过大导致目标端每批次写入时间长,行锁和表锁竞争加剧;大事务也拖慢了 check point 和日志切换。解决:把batch_size回调到 3000 到 5000,观察目标端写入耗时;必要时对大表开启并行分片迁移,但要注意分片键不能选分布不均的字段。
现象四:增量同步追平后,业务写入报主键冲突。原因是结构迁移时自增序列没有被同步,目标端自增起始值落后于源端。比如源端 user 表 id 已经到 100 万,目标端序列从 1 开始,增量回放插入 100 万的新行,再切流量后业务插入新记录撞上已存在的主键。解决:全量迁移完成后,立即重置目标端对应序列到源端当前自增值加一个安全余量;在切换验证清单里加上一条「检查目标端自增值大于源端当前最大值」。
现象五:datetime(6)的高精度字段迁移后丢失了微秒后缀。原因是目标端字段类型不支持微秒精度,或结构转换时映射到了精度较低的日期类型。这类问题最容易伪装成「没报错」,直到业务做时间范围查询时对不上账。解决:结构转换前对照两端数据库的类型映射规则,凡是时间精度字段、大精度数字字段,逐表确认转换后的类型;用小表试迁一次并跑那条最精确的时间查询,验证通过再放大到全量。
6. 进阶:把迁移任务做成可重跑、可校验、可交接的最后一公里
迁移工具上了线,真正拉开团队差距的是收尾这一步。我会把整套方案固化到三个层面。第一是幂等重跑能力:全量任务固定带truncate_target: true,增量任务靠数据库表里的 checkpoint 位点,任务被调度系统杀了或机器重启了,重启服务后能直接从上次消费位点继续。第二是校验自动化:把行数比对、主键极值比对、抽样字段比对写成一个脚本,每次迁移跑完自动执行并输出差异清单,而不是靠人肉眼对两个查询结果。第三是监控接入:全量任务监控表级失败数量,增量任务监控同步延迟峰值,超过阈值就告警,避免数据断流一晚上没人知道。
我最想分享的一个教训是:第一次带团队做割接时,我以为校验脚本就是跑一遍 count,结果上线第二天业务方说历史订单的金额字段对不上。查下来是那批订单在源端有字符集特殊符号,目标端转换时静默截断。从那以后,不管工具多信任,校验脚本里必须包含对关键业务字段的逐行哈希比对,哪怕只比对近一周的数据。做迁移同步,真正让你敢按下切换按钮的,不是工具的宣传文档,而是那套你自己反复跑过的校验。技术方案的最后一公里永远是笨功夫,但笨功夫最可靠。希望这篇能帮你少熬几个凌晨,一次切换就平平稳稳落地。
本文还有配套的精品资源,点击获取