最近有个需求让我挺头疼:公司里两套业务系统各用一套MySQL,订单库在A环境,BI报表库在B环境。产品那边要求每天凌晨能看到实时订单,但不允许直接改A库的链接,更不允许业务系统直连报表库。说白了就是要把订单表持续同步到另一个MySQL实例里,既要把历史数据一次性搬过去,又得让新产生的数据自动跟上。
我用Dbsyncer把这件事完整趟了一遍,mysql to mysql的全量和增量都调通了。整个过程其实没有想象中复杂,但有些细节是一路踩坑才明白的,所以把整套配置流程和心得整理出来,供后面要接数据同步的朋友参考。
1. 为什么要用Dbsyncer,而不是自己写脚本
先交代一下背景。我这次的目标很简单:把源MySQL里的订单表、用户表、支付流水表,同步到另一个MySQL里,供报表查询使用。表数量不算多,二十来张,但数据量在持续增长。刚开始我确实想过自己写Java定时任务,每天早上跑一次全量,后来细想了一下发现根本不划算。
全量同步有个天然缺陷:如果业务数据全天都在变,每天跑一次全量意味着报表永远滞后一天,想查当天18点的实时数据基本没戏。就算把任务调成5分钟一次,每次都是一整张表的DELETE+INSERT,对源库的压力特别大,还会把binlog刷爆。更麻烦的是,多张表之间如果有关联,全量顺序控制不好就会产生脏数据。
这个场景最合适的方案其实是增量同步,也就是让工具去监听源MySQL的binlog,每次只把新增、修改、删除的数据变化搬到目标库。市面上常见的工具我也对比过,这里直接说结论。
| 方案 | 全量能力 | 增量能力 | 部署成本 | 界面操作 | 适合人群 |
|---|---|---|---|---|---|
| 自己写定时脚本 | 有 | 基本没有 | 高 | 无 | 不想引入第三方依赖 |
| DataX | 强 | 弱,需要额外配合 | 中 | 无,写json | 一次性离线迁移 |
| Canal | 需另写客户端 | 强 | 中高 | 无 | 需要二次开发的团队 |
| Dbsyncer | 有 | 有 | 低 | 有Web界面 | 想快速落地的个人或小团队 |
如果你只是做一次性的库迁移,DataX确实很顺手,全量跑完就结束。但要长期保持两个MySQL实例的数据一致,DataX就得配合定时调度和增量SQL条件来拼,写起来挺折腾。Canal是另一个方向,它更偏底层,适合你打算自己造一套同步系统的时候用,但你得自己处理消息投递、断点续传、目标库写入这些逻辑。
Dbsyncer这块的优势在于它把全量和增量都放到一个Web界面里了。数据源配好之后,你要同步哪张表、字段怎么映射、是全量还是增量,在页面上点一点就行,不需要写代码。这个“玩玩就能用”的体验,对不是专门做数据同步基建的小团队来说,是真的很省事。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 安装前的准备与快速启动
2.1 先搞清楚Dbsyncer依赖什么
Dbsyncer是用Java写的,所以运行环境必须要有JDK。我用的是JDK 8,这个版本兼容性最稳,官方文档对JDK 8的支持也最充分。你要是机器上已经装了更高版本的JDK,理论上也能跑,但如果遇到启动报错,先不要怀疑工具本身,回头检查一下JDK版本往往更有效。
源库MySQL这边有个硬性前提:要做增量同步,必须开启binlog,而且日志格式得是ROW模式。这个后面我会专门展开,先记住一个检查命令:
sql复制SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'binlog_format';
SHOW VARIABLES LIKE 'binlog_row_image';
如果log_bin是OFF,那Dbsyncer只能做全量,增量功能是玩不起来的。这一步在你装Dbsyncer之前就应该先确认好,不然后面配置到一半才发现源库没开binlog,会比较难受。
2.2 三种安装方式,照着做就行
我自己实际用过两种方式,一种是直接下载发行包在Linux服务器上跑,另一种是在内网开发机上用Docker临时起一个来验证。这两种方式各有适用场景,我分别说一下。
Linux服务器上跑的话,先从Dbsyncer的Gitee或GitHub Release页面下载最新的tar.gz包,比如Dbsyncer-x.x.x.tar.gz,然后解压启动:
bash复制tar -zxvf Dbsyncer-x.x.x.tar.gz
cd Dbsyncer/bin
sh startup.sh
启动完成后,在浏览器里访问:
text复制http://服务器IP:1860
默认端口是1860,管理后台的默认账号密码都是dbsyncer。我第一次没看文档直接猜了admin,结果登不进去,后来才发现默认账号就是Dbsyncer这个名字本身。
Windows环境更简单,下载zip包解压之后,进入bin目录双击startup.bat就行。如果你电脑上没装JDK,那就先装一个,并把JAVA_HOME环境变量配置好,否则startup.bat会直接闪退。
Docker方式算是比较适合临时体验的:
bash复制docker run -d --name dbsyncer -p 1860:1860 dbsyncer/dbsyncer
镜像名要以官方仓库里的实际名称准,不同版本可能不一样。用Docker的好处是省去配JDK的步骤,但注意把数据目录挂载出来,不然后面配置的同步信息都写在容器里,容器一删就全没了。我是用-v参数挂载宿主机目录的,这样哪怕容器重建,配置和日志还能保留。
注意:Dbsyncer解压目录名不要包含中文和空格。我第一次放在
/data/同步工具/下面,启动时日志一直报路径异常,换到纯英文目录就好了,这种小问题排查起来特别费时间。
2.3 驱动管理这一步很少有人提前说
进入管理后台后,先别急着创建同步任务,有一个前置操作需要先完成:把MySQL驱动加到Dbsyncer里。Dbsyncer本身不自带所有数据库驱动,需要你手动上传对应版本的JDBC驱动jar包。
在驱动的管理页面,选择新增驱动,然后把mysql-connector-java的jar包上传进去。版本选择有个基本规律:MySQL 5.6/5.7用5.1.49的驱动最稳,MySQL 8.0及以上建议用8.0.x的驱动。我踩过的坑是把5.1的驱动拿去连8.0的MySQL,报错提示认证方式不支持,后来换成8.0的驱动才正常。
这一步很多人容易忽略,导致后面测试连接一直失败。你只要记住:驱动版本尽量对齐数据库大版本,宁可新一点不要旧太多。
3. 全量同步一次搞定
3.1 理解Dbsyncer里的连接器和同步器
在开始配同步之前,建议先把Dbsyncer的几个核心概念搞明白。它不至于很抽象,但如果你不清楚,后面界面里会看得一头雾水。
简单说,连接器就是数据源连接。你至少得建两个连接器,一个指向源MySQL,一个指向目标MySQL。连接器保存的是IP、端口、账号、密码、数据库名这些信息。同步任务在执行时,本质就是从源连接器读数据,写到目标连接器。
同步器就是真正干活的那个任务。它会把源连接器和目标连接器串起来,告诉你同步哪张表、按什么频率同步、是全量还是增量、字段怎么对应。我的理解是,连接器是管道两端,同步器是管道中间的水泵。
这也是Dbsyncer和DataX最大的不同。DataX的json配置是一锤子买卖,跑完就结束。Dbsyncer的同步任务是常驻的,你可以随时到Web界面启动、暂停、修改配置,同步状态是长期维护的。
3.2 创建源库和目标库连接
我这里以两个MySQL实例为例,源库在192.168.1.10,目标库在192.168.1.20。
在连接管理页面点新增,配置源连接,要点如下:
- 连接名称:mysql-source-orders,自己起个好认的名字
- 数据库类型:MySQL
- 地址:192.168.1.10
- 端口:3306
- 数据库名:orders
- 用户名:sync_user
- 密码:你自己设置的
目标连接类似,数据库名填ods_orders,用户名和密码用目标库的账号。
建议数据库账号不要直接用root。我一般是单独建一个同步专用账号,只授予同步需要的权限。全量同步至少需要SELECT权限,增量同步还需要REPLICATION SLAVE和REPLICATION CLIENT。MySQL 8.0里可以这样创建:
sql复制CREATE USER 'sync_user'@'%' IDENTIFIED BY '你的密码';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'sync_user'@'%';
FLUSH PRIVILEGES;
实测用最小权限账号好处很多,就算密码泄露也不会把源库搞出大问题。
两个连接建好之后,先分别测试一下连通性。测试失败的话,优先检查驱动版本、账号权限、还有源库防火墙是否放行了3306端口。Dbsyncer的报错信息一般会直接告诉你是密码错误还是网络不通,按提示一步步排查就行。
3.3 用正则匹配多张表,而不是一张张加
连接没问题之后,就可以新建同步任务了。我第一次傻乎乎地一张表建一个任务,搞了十来个任务后觉得不对劲:以后加表怎么办?每个任务单独维护太痛苦了。
Dbsyncer的表选择支持正则表达式,这是一个很省事的特性。比如我想把源库里orders库下所有以orders_开头的表都同步过去,就直接在表名位置填:
text复制orders_.*
如果需要精确同步某几张表,用管道符号隔开也可以:
text复制order_info|pay_record
这个正则能力在日常维护里特别实用。比如新增了一张orders_refund表,只要它符合前缀规则,Dbsyncer在任务扫描时会自动纳入同步范围,不需要我再手动建任务。
不过在同步开始之前,我建议你先把目标库的表结构建好。Dbsyncer在目标库自动建表方面不是万能的,尤其是字段类型和索引,自动建出来的往往不是你想要的。最好的做法是先把源库的表结构导出,然后在目标库手动执行建表语句,再去配置同步任务。字段映射这一步,追求的是“人可控”。
3.4 启动全量任务并验证
同步方式选择全量,配置好全量同步的周期之后,感觉默认的15秒对我来说拉取频率不高,用默认的15秒开始测试。启动同步任务,页面上会显示任务的执行状态和同步数据条数。
为了验证是否真的同步成功了,我通常在源表里插入几条带明显标记的记录,然后在目标表里查一下:
sql复制SELECT COUNT(*) FROM 目标库.目标表;
SELECT * FROM 目标库.目标表 WHERE id IN (...);
如果查询出来的数据量和源表一致,内容也没有缺字段,就说明全量同步链路是通的。如果目标表缺数据,先在日志里找同步器的执行记录,重点看有没有SQL执行错误。常见原因大多是字段类型不兼容、目标表缺少某个NOT NULL列导致写入失败、字符集不一致导致乱码等。
4. MySQL binlog增量同步配置
4.1 binlog为什么这么重要
全量同步完成后,订单表里之前的历史数据已经到目标库了。但新的订单还在不断产生,所以必须上增量同步。Dbsyncer增量同步的原理是读取源MySQL的binlog日志,把它解析成一条条数据变更记录,再在目标库重新执行。听不懂没关系,我换个说法。
binlog就是MySQL自己记的一本流水账,只要数据有变化,MySQL就把变化的过程追加记录到binlog文件里。Dbsyncer相当于一个专门盯着流水账看的会计,每当账本上多了一笔记录,他就根据这笔记录去更新目标库。
要让Dbsyncer能看懂这本流水账,binlog的格式必须用ROW模式。ROW模式和STATEMENT模式的差别在于:STATEMENT模式只记录“执行了哪条SQL”,比如UPDATE orders SET status = 2 WHERE id = 100,但Dbsyncer并不知道id=100那行数据原来长什么样;ROW模式直接把“改动后这一行每个字段的值、改动前每个字段的值”都写到binlog里,这样Dbsyncer就拿到了完整数据,可以精准复制。
所以,如果你以后只是做数据归档或简单集群复制,STATEMENT模式效率高一些,但要让外部工具实时感知每行数据变化,必须切ROW。切binlog格式需要修改MySQL配置文件my.cnf,在[mysqld]节点下加下面这些参数:
ini复制[mysqld]
server-id = 1
log-bin = mysql-bin
binlog_format = ROW
binlog_row_image = FULL
expire_logs_days = 15
其中binlog_row_image = FULL强烈建议显式配置。默认值是FULL,但如果之前被改成MINIMAL,binlog里只会记录发生变化的那几列旧值,其他列的数据Dbsyncer拿不到,同步出来的数据就不完整。改完配置后重启MySQL,再确认一下:
sql复制SHOW VARIABLES LIKE 'binlog_row_image';
如果看到FULL,就说明配置生效了。
4.2 增量同步任务的关键配置
源MySQL的binlog就绪之后,回到Dbsyncer界面新建一个同步任务,这次同步方式选择增量。Dbsyncer会自动定位当前源库的binlog文件和position位置,之后每当源库数据发生变化,Dbsyncer就能从上次记录的位置继续读,不会重复也不会漏掉。
配置增量同步时,Dbsyncer会在目标表上维护一个操作类型字段。也就是说,目标表除了原来订单表的字段之外,会多出一个类似op_的字段,用来标记这一行是被插入的、被更新的还是被删除的。对应的值一般是i、u、d这样的字符。如果你同时想知道某条数据是从哪张表同步过来的,Dbsyncer也支持配置一个来源表名字段。这种设计在实际做数据审计的时候非常好用,你可以直接把源表、目标表、变更类型都查出来。
我自己在建表时会预留这个字段:
sql复制ALTER TABLE 目标库.目标表 ADD COLUMN op_ VARCHAR(4) DEFAULT NULL;
如果你不打算手动加,那就给Dbsyncer对应的建表权限,由它自己处理,但我始终推荐手动建表。手工预留字段可以让你完全掌握表结构,避免生产环境出现意外的DDL。
4.3 存量数据先全量,增量数据再跟上
增量同步听起来很爽,但有一个绝对别踩的坑:如果目标库是空表,你直接开启增量同步,那历史的数据永远不会出现在目标库,因为binlog只记录开启之后的新变化,不会帮你去扫盘补旧数据。正确顺序是先做一次全量同步,把存量数据搬到目标库,再开启增量同步,让增量任务从全量完成的那个时间点开始接棒。
Dbsyncer做增量时一般会保存当前binlog位置,所以理论上可以做到不丢不重。但为了验证,我建议在启动增量任务后,在源库依次执行三种操作:
sql复制INSERT INTO 源库.订单表 (id, order_no, amount, create_time) VALUES (10001, 'TEST001', 99.00, NOW());
UPDATE 源库.订单表 SET amount = 199.00 WHERE id = 10001;
DELETE FROM 源库.订单表 WHERE id = 10001;
然后分别去目标表查对应记录。插入后目标表多了一行,更新后金额变成199.00,删除后目标表那一行消失了,说明整个增量链路是通的。
这里需要注意一点:如果你的删除策略不是物理删除,而是把状态字段置为已删除,那Dbsyncer同步过来的也会是更新操作,不会触发delete标记。只有真的执行了DELETE语句,binlog里才会有delete事件。
另外,增量同步非常依赖主键。Dbsyncer要定位目标表中对应记录的更新和删除,必须靠主键或者唯一索引,如果表上没有主键,它很难精确找到要改的是哪一行。所以源表最好都有主键,目标表的主键结构也要跟源表一致。
5. 进阶配置与日常维护心得
5.1 不同表结构之间的字段映射处理
实际同步时,源表和目标表的字段往往不完全一致。比如源表有user_name和phone两个字段,目标表把这两个字段合并成了contact_info,或者目标表比源表少几个字段。这种情况下直接同步肯定是会报错的。
Dbsyncer提供字段映射配置,你可以手动指定源表的哪个字段对应目标表的哪个字段。比如:
- 源字段
user_name映射到目标字段contact_name - 源字段
phone映射到目标字段contact_phone - 源字段
status同步时直接忽略
忽略字段的情况很常见。例如源表有些内部状态位,对报表没有意义,就在映射时勾掉,不往目标表写。这样做既节省目标库空间,也减少后续数据字段不一致带来的困扰。
如果你的目标表比源表多出一些需要填充默认值的字段,那就要在目标库里先把默认值设置好。SQL执行阶段遇到NOT NULL且无默认值的列会直接报错,这是字段映射里最常见的坑。
5.2 任务调优和资源控制
Dbsyncer默认的同步速度已经能满足大部分场景,但如果单表数据量特别大,就要适当调整同步批次大小和线程数。批量写入能显著减少网络往返次数。我自己的经验是,批量提交的条数不要盲目求大,过大的批次会导致单次事务执行时间变长,源库和目标库的锁冲突概率增加。一般控制在500到1000条比较稳妥。
增量同步的实时性,从数据产生到目标库可见,正常可以做到秒级延迟。如果你发现延迟越来越高,先检查是不是某个慢SQL把目标库拖住了,再看源库binlog文件产生速度是否大于Dbsyncer消费速度。如果是后者,需要适当调大消费线程数,而不是无限扩大批次。
配置任务周期时不要太激进。增量同步还好,Dbsyncer是事件驱动的;全量任务如果周期太短,可能上一次还没跑完,下一次又开始了。全量任务适合低峰期执行,或者配合过滤条件只同步当天变动的数据。
5.3 日常监控看哪里
Dbsyncer的Web界面会显示任务运行状态,包括连接器是否在运行、同步条数、最后同步时间、报错信息等。我把同步任务的状态页面当作主要监控入口,每天偶尔看一眼。
在Linux服务器上可以查看日志,例如:
bash复制tail -f Dbsyncer目录/logs/dbsyncer.log
如果发现日志里有Exception或ERROR,不要慌,先看完整的堆栈信息,再定位是哪一步出了问题。大部分错误其实都是连接、权限、表结构这老三样,能在日志里找到具体信息。
6. 常见问题与排查技巧实录
下面这些是我实际操作中遇到过的,以及帮朋友排查时看到过的高频问题,整理成一张速查表:
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
| 全量同步没有数据写入 | 源表为空或正则没有匹配到表 | 先在源库执行SQL确认表数据量,再检查表匹配正则 |
| 增量任务启动后不生效 | 源库没有开启binlog,或binlog_format不是ROW | 修改my.cnf并重启MySQL,确认binlog配置 |
| 更新操作没同步过去 | 目标表没有主键或唯一索引 | 给目标表补主键或唯一键 |
| 删除操作没同步过去 | binlog_row_image不是FULL或无主键 | 确保binlog_row_image=FULL,目标表有主键 |
| 源库密码正确但连接失败 | MySQL驱动版本太旧 | 换成和MySQL大版本匹配的JDBC驱动 |
| 中文或emoji变成问号 | 字符集设置不一致 | 源库/目标库表统一utf8mb4,JDBC连接串加上characterEncoding=utf8 |
| 任务停很久后无法续传 | binlog文件被清理 | 调大expire_logs_days,必要时重新全量再增量 |
| 同步到一半报字段不存在 | 源表或目标表结构变更 | 手工同步表结构,再更新字段映射 |
排错时有一个习惯值得养成:先看数据源连接是否正常,再看表结构是否匹配,最后才怀疑Dbsyncer本身的bug。大部分情况下,问题都在前两个环节,Dbsyncer只是忠实地把数据库层的错误暴露给你了。
还要提醒一下Linux下MySQL表名大小写的问题。源库如果在Windows里开发,表名可能是OrderInfo,而目标库在Linux上,lower_case_table_names默认是0,表名大小写敏感。这种情况同步任务可能报找不到表。建议把所有业务表名统一成小写,这是最稳妥的规范。
我在实际配置中还有一个习惯:每次改完同步任务,尤其是改完字段映射或表匹配规则之后,先暂停任务再修改,改完重新启动。千万不要在任务运行状态下手动频繁改配置,容易造成同步状态错乱,虽然Dbsyncer本身的报错恢复能力很好,但没必要给自己找麻烦。
另外,Dbsyncer占用的内存不高,给个1GB左右的堆内存就非常宽裕了,但如果你同时同步几十张表,还要开启很多连接器,那就稍微调大一点。启动脚本在bin目录下,调整JVM参数后再重启,不要在生产环境频繁改,每次改完要做一次同步验证。
Dbsyncer对我这种既要快速落地,又不想维护一堆代码的人来说,确实是个顺手的选择。它的全量同步能解决历史数据初始化,增量同步能解决实时数据更新,两边配合起来,一套简单的mysql to mysql数据管道就搭好了。如果你后面数据量变大,需要同步到Elasticsearch或者Kafka,Dbsyncer也支持这些目标端,完全可以基于现有的连接器继续横向扩展,不用推翻重来。
