ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

DBSyncer数据同步中间件实战:全量增量同步与插件配置

DBSyncer数据同步中间件实战:全量增量同步与插件配置 简介DBSyncer简称dbs是一款开源的数据同步中间件面向数据库管理员、开发工程师与运维人员主要解决MySQL、Oracle、SqlServer、PostgreSQL、Elasticsearch、Kafka、File、SQL等多源异构数据之间的实时同步、迁移与分发问题。除内置常见同步场景外还支持上传插件自定义同步转换逻辑并提供全量/增量数据统计图与应用性能预警便于对同步链路进行日常监控与调优。该资源包共737个文件主体为472个Java源码文件配合XML、properties等配置文件可理解核心同步流程另有81个PNG图片、59个CSS样式表、42个HTML页面和36个JS文件构成可视化管控前端静态资源还有SQL脚本、Shell/Bat/CMD启动脚本等便于本地快速部署调试。从文件构成看既包含后端同步引擎实现也包含前端监控面板整体属于可运行、可二次开发的完整工程。压缩包整体仅2.07MB结构紧凑适合作为学习数据同步中间件设计思路的入门范本。目前已有747人学习下载适合需要搭建数据同步服务或研究其插件扩展机制的参考。1. 数据同步中间件 DBSyncer先想清楚它到底同步什么做数据同步的人多半经历过这种纠结库多了之后MySQL、Oracle、SqlServer、PostgreSQL 各存一摊业务要的是实时汇总人力要的是别再写一堆重复的抽取脚本。市面上工具不少但要么只覆盖增量 binlog要么配置复杂到让人劝退。DBSyncer简称 dbs是那种“打开就能用”的开源中间件自带 Web 管理界面不用装 Agent也不用写一大堆 XML 任务描述它把全量同步、增量同步、自定义转换插件、监控预警都塞进一个包里。适合的场景很明确带有生产库和业务库之间的数据流转或者多数据源向 Kafka、ES、文件落地同步的从业者。这篇文章我们直接拆它的工程配置和同步链路讲清楚怎么落地、参数怎么调、坑在哪。2. 同步架构与插件机制全量、增量、自定义转换怎么拼在一起2.1 核心模块拆解连接器、转换插件、统计监控DBSyncer 的同步链路可以粗略分成三段连接器负责跟各个数据源打交道映射器负责把源表的行数据转成目标端能识别的结构插件负责在数据流转过程中做转换。这三段各自独立所以你可以在不改动主流程的情况下只针对某一张表或者某一个字段类型做定制。连接器部分内置了 MySQL、Oracle、SqlServer、PostgreSQL、ES、Kafka、File、SQL 这几类。每个连接器解决的问题不一样MySQL 走的是 binlog 解析Oracle 走的是日志挖掘或者触发器机制而 File 连接器则是把文件当成数据源来读。实际用的时候有一个容易被忽略的点连接器只是负责“连通”它并不保证数据类型能一一对应。比如 Oracle 的 NUMBER 到 PostgreSQL 的 NUMERIC宽度和精度在某些边界值上会出问题这种问题不体现在同步日志里而是体现在目标表的数据准确性上。转换插件是 DBSyncer 比较核心的设计。它允许你上传自定义 jar 包在数据同步过程中做逐行或者逐批处理。常用的场景包括字段加解密、状态值映射、多表关联填充。插件机制的本质是让同步链路变成一个可编程的管道不是说非得在 SQL 里左联右联把数据先算好再同步。监控统计这一块Web 界面里能看到全量和增量两条数据统计图另外有应用性能预警可以针对同步延迟、错误率、内存占用做阈值设置。预警目前是页面展示加日志输出不会主动推消息所以生产环境一般要配合外部监控系统把日志接走。2.2 插件扩展点自己写同步逻辑时改哪里很多人在第一次接触 DBSyncer 时会问我到底在哪个环节写转换逻辑答案是两个位置一个是映射器的转换表达式里另一个是自定义插件里。映射器转换表达式适合做简单的一对一转换比如字段改名、常量填充、时间格式化。自定义插件则适合做有状态或者需要外部依赖的转换比如查另一张表做映射、调内部接口补数据。自定义插件需要实现它提供的转换接口打成 jar 包上传到 Web 界面的插件管理里然后在映射步骤里选择该插件即可。接口设计上它把“同步前处理”和“同步后处理”分开了。同步前处理在数据写入目标前执行同步后处理在写入完成后执行可用来做数据补偿或者关联表的更新。这里有一个实际开发中的注意点同步前处理里不要做耗时太长的 IO 操作因为它是逐批调用的如果处理时间超过增量同步的批处理间隔容易造成同步延迟堆积。常见做法是把耗时操作放到同步后处理或者把这个转换逻辑拆成独立的计算服务DBsyncer 只做透传。引擎层面同步任务被拆成了一个个 pipeline每个 pipeline 管理一批映射。你可以把同一张源表的多个目标库映射放到一个 pipeline 里也可以每一对库单独建任务。我的习惯是按业务域分 pipeline而不是按库分。因为一个业务域的表改动往往是一起发生的集中在一个 pipeline 里排查问题会更快。3. 落地配置从启动脚本到双库全量增量同步3.1 启动与初始化工程里那几个脚本文件是干什么的拿到 DBSyncer 的发布包后你会在根目录看到几个关键文件startup.bat、version.cmd、build.cmd、jmxremote.access。它们看起来不起眼但启动排错时都用得上。startup.bat 是 Windows 下的启动入口它内部会读取同目录下的配置环境变量然后拉起 Java 进程。Linux 环境下一般用同名的 shell 脚本启动但早期版本里 shell 启动脚本和 bat 脚本对环境变量的处理有一些差异这个后面避坑章节再详细说。version.cmd 用来打印当前构建版本和编译时间排查问题时首先要确认对方跑的是哪个版本不然某些 bug 的定位方向完全是反的。build.cmd 是编译脚本它的用途不是给使用者跑而是给二次开发者重新打包用的里面通常会附带依赖仓库地址。jmxremote.access 是 JMX 远程监控的权限文件。生产环境如果要接 Prometheus 或者 JConsole 做 JVM 监控就需要配置 JMX 相关参数这个文件定义了哪些用户具备读写权限。默认情况下它只允许本机访问如果你需要远程监控启动参数里得加 -Dcom.sun.management.jmxremote.port 等配置。启动 DBSyncer 的命令如下# 进入安装目录 cd /opt/dbsyncer # 查看版本确认构建是否正常 ./version.sh # 前台启动调试用日志直接输出到终端 ./startup.sh # 后台启动生产环境常用 nohup ./startup.sh logs/dbs.log 21 启动之后浏览器访问管理界面默认端口是 1860。注意第一次访问会要求初始化管理员账号这个账号密码是存在本地数据库里的如果弄丢了只能去安装目录下的数据文件里重置没有命令行找回的入口。3.2 配置 MySQL 到 PostgreSQL 同步全量参数与增量参数这是最典型的一套同步场景源库 MySQL 8.0目标库 PostgreSQL 14两边的表结构已经通过其他工具同步过了现在需要定期做增量数据同步同时第一天先跑一次全量作为基线。登录 Web 界面后先建连接器。MySQL 连接器需要填主机、端口、库名、用户名、密码外加一个读写一致性相关的参数。PostgreSQL 连接器同理。这里有一个容易踩的坑MySQL 连接器里的 server-id 参数。如果你配置的值和同一个 MySQL 实例上其他 binlog 消费端比如 Canal 或者其他同步工具的 server-id 冲突MySQL 会主动断开连接日志里表现为“Got fatal error 1236”。常规做法是给每一个消费端分配一个唯一的 server-id建议在 100 到 2^32-1 之间选一段不容易撞车的区间。连接器建好后进入映射管理创建映射-- 源端表结构MySQL CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(64) NOT NULL, user_id BIGINT NOT NULL, amount DECIMAL(12,2) NOT NULL, status TINYINT NOT NULL DEFAULT 0, created_at DATETIME NOT NULL ); -- 目标端表结构PostgreSQL CREATE TABLE orders ( id BIGINT PRIMARY KEY, order_no VARCHAR(64) NOT NULL, user_id BIGINT NOT NULL, amount NUMERIC(12,2) NOT NULL, status SMALLINT NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL );映射配置里需要指定源表、目标表、以及字段之间的对应关系。字段名相同的情况下可以自动映射但不建议完全依赖自动映射因为数据类型差异比如 MySQL 的 DATETIME 到 PostgreSQL 的 TIMESTAMP虽然 DBSyncer 会自动归一化处理精度上可能有微秒级的截断差异。关键的同步参数有三个增量同步模式建议选“日志解析”因为定时轮询模式有时间窗口数据一致性弱。批提交大小batchSize默认 1000 条一批这个值不是越大越好。批量太大时单次事务的执行时间变长目标库的 WAL 膨胀会加剧而且一旦失败回滚的代价也更大。一般建议 500 到 2000 之间根据目标库的写入性能来调。增量延迟阈值delayThresholdMs用于触发预警的延迟阈值影响 Web 界面的统计展示不直接影响同步行为。3.3 验证最小闭环同步状态怎么看配置完成后不要直接开启增量先做一次全量同步验证链路通不通。在全量同步界面点击“开始全量”之后任务会进入运行状态界面上有实时计数器。全量同步完成之后手动在源库里插入一条数据INSERT INTO orders (order_no, user_id, amount, status) VALUES (TEST001, 1001, 99.99, 1);然后等待几秒在目标库确认SELECT * FROM orders WHERE order_no TEST001;这条能查到说明增量链路已经通了。这里有一个细节全量同步和增量同步是两条独立的调度链路全量跑完并不会自动切换成增量需要手动开启增量任务。如果忘了开增量线上数据就会堆积在源库等到你想起来去开增量时日志文件可能已经被 MySQL 清理了那就会面临补数据的尴尬。所以每次配置完同步任务全量完毕之后必须立刻检查增量任务状态。查看 DBSyncer 本身的日志也有讲究路径在 logs 目录下分为系统日志、同步日志、错误日志三类。同步日志里能看到每条映射的消费延迟和吞吐量如果同步慢先看这个文件不要一上来就查 JVM。4. 避坑与排查同步对不上、延迟高、插件不生效怎么办4.1 增量同步中途断掉重启后数据“回跳”或“丢失”现象MySQL 到 PostgreSQL 的增量任务跑了一周某天发现目标库的数据比源库少了十几条日志里没有任何报错。原因日志解析模式下DBSyncer 把消费位点记录在本地的元数据表里。如果目标库写入失败且重试次数用尽该批次会被暂时跳过后续新的 binlog 事件会覆盖掉旧位点。也就是说跳过的事件没有被“记住”重启后不会重新消费。解决先确认目标库写入失败的类型。如果是字段约束冲突比如唯一键重复要把这些冲突处理掉再把同步任务重建一次或者把位点回退到冲突发生之前的时间点。操作路径是管理界面的任务配置里找到“同步位点”手动输入对应的时间戳或者文件偏移量。回退位点是一个危险操作一定要先确认源库 binlog 还保留着那个时间点的日志否则回退之后依然会断。4.2 Oracle 大表全量同步总是中途报错索引失效现象从 Oracle 同步一张 2000 万行的流水表每次跑到 1200 万行左右就断开错误信息显示连接超时或者游标关闭。原因全量同步默认使用 JDBC 流式读取但当表数据量大且网络不稳定时JDBC 连接的 fetchSize 设置太小会导致读取超时。Oracle 的游标在长时间未消费时会被数据库主动关闭。解决全量同步任务的读取端参数里调大 fetchSize 到 5000 或 10000同时给 JDBC 连接设置 socketTimeout 为 0表示不超时。这两个参数在连接器的“高级配置”里都有不要只在连接串上改。另一个技巧是把全量任务按主键范围拆成多个子任务每个子任务跑 500 万行左右互不干扰任何一个失败重跑那一段就行。4.3 自定义插件上传后不生效映射步骤里找不到现象插件管理里明明上传成功了但在映射配置的插件下拉框里看不到这个插件。原因DBSyncer 的插件是绑定连接器类型的。你上传的插件如果内部声明支持的数据源类型和当前映射的源端不一致界面会自动过滤掉。另外一个原因是上传插件后需要等待热加载完成如果 jar 包太大或者里面有静态初始化逻辑加载时间会超过界面提示的时间。解决插件开发时把支持的连接器类型标注清楚不要写一个万能的插件名。上传之后去系统日志里搜“plugin”关键字确认加载结果。如果看不到任何相关日志多半是 jar 包结构不完整重新打包时注意不要把依赖打进同一个 jar除非你用了 shade 插件。热加载失效时最稳妥的办法是重启一次同步服务不用重新建任务任务会重新加载插件。4.4 Kafka 目标端同步延迟持续走高消费组却空闲现象同步数据到 Kafka 的链路界面统计显示延迟从 10 秒一路涨到 30 分钟但 Kafka 端的消费组 lag 为 0说明下游消费者是空闲的。原因目标端 Kafka 生产者的 batch.size 和 linger.ms 配置不合适。DBSyncer 默认按批发送如果 batch.size 太大且 linger.ms 太小会导致每一批都要等很久才能攒满而下游已经消费完了生产者这边还在攒数据。界面统计里的延迟计算的是生产者端的积压不是 Kafka 端到端的延迟。解决调整同步任务中 Kafka 生产者参数把 batch.size 从默认值降到 16KB 到 64KB 之间同时把 linger.ms 设为 10 到 50 之间让每一批数据既能快速发出又不至于太碎。这个方法只调同步任务不需要改 Kafka 集群配置。4.5 目标库表结构里多了几个默认值字段同步一直报“列数不匹配”现象源表有 6 个字段目标表有 8 个字段其中两个字段有数据库默认值同步任务启动后直接报错。原因默认的自动映射逻辑要求字段一一对应。目标表多出来的字段如果没有在映射关系里配置就会被当作异常。解决创建映射时把目标表多出的两个字段配置为“常量”转换值设为 default让 DBSyncer 在写入时不主动填充交给数据库的默认值机制处理。或者手工在映射里勾上仅同步指定字段这样就不会校验未映射字段。5. 进阶技巧把监控预警和性能瓶颈一起收进日常习惯里DBSyncer 的监控页面提供了全量和增量的数据统计图但真正到生产环境里这个页面上的数字只是第一道防线不能只依赖它。一个实用的做法是把 DBSyncer 的日志接入外部的日志收集链路比如 ELK 或者 Loki然后在日志里精确匹配同步错误的异常输出设置独立的告警规则。它自带的应用性能预警功能只会在 Web 页面和日志文件里留痕不会主动调用企业微信或钉钉的 webhook所以外部告警必须自己接。性能调优上面我一般习惯分三步走。第一步先看目标库的写入延迟用数据库自身的慢查询日志来定位是不是目标库的索引过多导致写入变慢。第二步看源库的 binlog 产生速率如果源库写入高峰期超过每秒 2000 行但目标库消费能力只有 500 行/秒这时候再怎么调 DBSyncer 参数也没用得从业务侧削峰。第三步才是调批提交大小和并行度。DBSyncer 的并行度控制是按映射数拆分的不是按线程数。如果你有 10 张表要做同步可以在一个 pipeline 里建 10 个映射这些映射是独立线程消费的。但如果这 10 张表同时写入同一个目标库且目标库的连接池较小反而会把目标库打挂。所以建 pipeline 的时候我会把表按写入量分组大表单独一个 pipeline小表合在另一个 pipeline 里。还有一个很多人不知道的习惯每次改完映射配置我会手动记录一条带有时间标记的源库数据然后在目标库查一遍确认这条数据经过所有转换插件之后仍然符合预期。因为插件逻辑改动了之后界面上的测试用例未必覆盖所有字段类型这种手动验证看起来笨但却是最有用的。最后说一个更隐蔽的问题DBSyncer 的元数据存储用于保存任务、映射、位点等本身也是一个小型数据库。如果你创建了太多历史任务且从不清理这个元数据库会逐渐膨胀导致管理界面响应变慢。我会每个月清理一次已废弃的映射和历史统计日志保证元数据存储保持在稳定大小。从那以后我每次上线新的同步任务都会强制走一遍“先建连接器、再建映射、全量跑一次、开启增量、手动验证一条、检查延迟曲线”这套流程一次都不省。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进