SpringBoot 集成 Canal 实现 MySQL 数据同步的实战配置与避坑指南
1. 为什么需要Canal实现MySQL数据同步想象一下你正在运营一个电商平台订单数据每秒都在疯狂增长。突然有个需求要把订单数据实时同步到数据分析库做报表这时候如果直接连主库查询分分钟把数据库拖垮。这就是Canal的用武之地——它像个小秘书默默记录MySQL的每笔改动再悄悄同步到别处。我去年帮一家物流公司做运单跟踪系统时就遇到过类似场景。他们原有系统直接读写主库高峰期经常卡死。接入Canal后查询压力全部分流到从库主库负载直接降了70%。这种基于日志的增量同步方式比传统的定时全量扫描优雅太多了。Canal的工作原理其实很简单伪装成MySQL的从库接收主库的binlog变更事件。但实际配置时你会发现不少坑比如多表同步的配置方式就特别反直觉。下面我会手把手带你避开这些坑。2. 环境准备与组件部署2.1 组件选型与下载首先到Canal的GitHub Release页面下载两个核心包canal.deployer负责监听MySQL的binlog变化canal.adapter将变更事件应用到目标库建议用1.1.7稳定版新版本可能有兼容性问题。我遇到过1.1.6的adapter连接RocketMQ超时的bug折腾了半天才发现是版本问题。wget https://github.com/alibaba/canal/releases/download/canal-1.1.7/canal.deployer-1.1.7.tar.gz wget https://github.com/alibaba/canal/releases/download/canal-1.1.7/canal.adapter-1.1.7.tar.gz2.2 MySQL配置关键点在MySQL的my.cnf中必须开启binlog并设置ROW模式[mysqld] log-binmysql-bin binlog-formatROW server_id1记得给Canal账号授权CREATE USER canal% IDENTIFIED BY canal; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO canal%;有次我漏了REPLICATION CLIENT权限deployer一直连不上还报错说密码错误坑了我两小时。3. Deployer配置详解解压后重点修改conf/example/instance.properties# 像侦探一样监听MySQL canal.instance.master.address127.0.0.1:3306 canal.instance.dbUsernamecanal canal.instance.dbPasswordcanal canal.instance.connectionCharsetUTF-8 canal.instance.filter.regex.*\\..*启动时如果报Address already in use可能是默认的11111端口被占用了。这时候可以# 查看端口占用 netstat -tlnp | grep 11111 # 修改conf/canal.properties里的canal.port我习惯用supervisor管理进程配置示例[program:canal_deployer] command/opt/canal/deployer/bin/startup.sh autostarttrue redirect_stderrtrue stdout_logfile/var/log/canal_deployer.log4. Adapter配置的隐藏技巧4.1 多表同步的坑官方文档没说清楚多表同步要复制配置文件。比如要同步user和order表复制conf/rdb/mytest_user.yml为mytest_order.yml分别配置不同的表名# mytest_order.yml示例 dataSourceKey: defaultDS destination: example dbMapping: database: ecommerce table: orders targetTable: analytics_orders targetPk: order_id: id4.2 字段映射黑科技当源表和目标表字段名不一致时可以用targetColumns配置dbMapping: targetColumns: user_name: name user_age: age遇到过字段类型转换问题试试sqlMap配置dbMapping: sqlMap: - SELECT id, CAST(amount AS DECIMAL(10,2)) AS amount FROM orders5. SpringBoot集成实战5.1 依赖配置的坑Maven要排除冲突的Logback依赖dependency groupIdcom.alibaba.otter/groupId artifactIdcanal.client/artifactId version1.1.5/version exclusions exclusion groupIdch.qos.logback/groupId artifactIdlogback-classic/artifactId /exclusion /exclusions /dependency5.2 事件处理最佳实践建议用工厂模式处理不同表的事件Component public class OrderEventHandler implements CanalEventHandler { Override public void handle(CanalEntry.RowChange rowChange) { // 处理订单变更逻辑 } Override public String getTableName() { return orders; } }在监听器中动态路由Autowired private ListCanalEventHandler handlers; private void handleEvent(CanalEntry.Entry entry) { String tableName entry.getHeader().getTableName(); handlers.stream() .filter(h - h.getTableName().equals(tableName)) .forEach(h - h.handle(rowChange)); }6. 性能调优经验6.1 批处理参数优化在canal.properties中调整canal.instance.memory.batch.modeMEMSIZE canal.instance.memory.buffer.size16384 canal.instance.memory.buffer.memunit10246.2 网络闪断应对给Connector加上重试机制CanalConnector connector CanalConnectors.newSingleConnector( new InetSocketAddress(host, port), destination, , ); // 带重试的连接方法 public void connectWithRetry(int maxRetries) { int retries 0; while (retries maxRetries) { try { connector.connect(); return; } catch (Exception e) { retries; Thread.sleep(1000 * retries); } } }7. 常见问题排查指南问题一adapter启动后没同步数据检查deployer日志是否有parse row data日志确认adapter的application.yml中destination名称匹配问题二同步延迟越来越高调整adapter的syncBatchSize参数建议500-1000检查目标库索引是否合理问题三字段值变成NULL检查yml中mapAll配置是否为true确认目标表字段允许NULL有次生产环境同步突然停止最后发现是有人改了MySQL密码但没更新canal配置。现在我会在配置中心加上监控密码变更时自动告警。