ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Canal的MySQL binlog实时同步方案与实践

基于Canal的MySQL binlog实时同步方案与实践 业务库里的数据不能只躺在 MySQL 里这句话做过后端的人应该都有感触。报表要读、搜索要读、数仓也要读读太多业务库扛不住更麻烦的是数据实时性要求越来越高凌晨跑批这种方案在业务面前根本交不了差。我之前接过好几回类似的同步需求中间也踩了不少坑最后在线上稳定跑起来的方案就是基于 Canal 解析 MySQL binlog 做增量同步。如果你也正被“怎么把 MySQL 的数据实时同步到另一套存储”这个问题卡住这篇从环境准备到落地配置的实战记录应该能帮你省下不少排查时间。1. 项目背景与方案选型同步需求不能靠“定时任务硬扛”1.1 我遇到的典型同步场景先说最近一次需求。业务系统的主数据在 MySQL 里每天新增几十万条订单记录同时需要把订单、用户基础信息同步到另外一个读多写少的统计库里供运营后台做实时查询。之前团队的做法是写一个定时任务每五分钟跑一次增量更新靠 updated_time 字段过滤。前期数据量小问题不大。等到订单量上来之后五分钟延迟带来的数据不一致开始被运营抱怨而且定时任务本身还会把业务库的连接池打满偶尔出现锁表报警。类似的需求往往集中在下面几个地方把 MySQL 数据同步到只读的 MySQL 备库或从库分担查询压力。将 MySQL 的变更实时推送到 Kafka下游继续接入 ClickHouse、Elasticsearch 或另一个 MySQL 库。做异构数据库迁移比如 MySQL 到 TDengine、到 HBase。这类需求有个共同点要求数据变化几乎实时可见并且不能侵入业务代码。如果靠业务代码里“双写”或者“MQ 发事件”对现有系统的改造量会非常大而且容易漏消息。1.2 几条常见同步路线的取舍我在方案评审阶段对比过几条主流路线。第一种是沿用 mysqldump 定时全量导入。适合数据量小、同步频率低的场景比如每天凌晨同步一次配置表。但全量导出对源库有一定压力持续全量跑会拖垮业务高峰期的性能而且无法保证实时性。第二种是 MySQL 原生主从复制。同步延迟低稳定性高适合把数据同步到另一台 MySQL 实例。但它有两个明显的限制一是目标端必须是 MySQL没办法直接解决 MySQL 到 ClickHouse、ES、Kafka 的同步问题二是主从复制是在 MySQL 内部机制层面完成的不方便在同步过程中对字段做过滤、转换或者分流。第三种是基于 binlog 解析的中间件方案。Canal 就是其中的典型代表。它把自己伪装成 MySQL 的从库向主库请求 binlog然后把解析好的增量数据按行事件输出。这个方案的好处是解耦不对业务表做任何结构改造也不需要在业务代码里埋点而且目标端可以非常灵活可以是另一个 MySQL、消息队列也可以配合 adapter 同步到多种存储。1.3 为什么我最终选了 Canal选 Canal 不光是看中它的能力也考虑到团队已有技术栈和运维成本。Canal 本身是一个 Java 服务部署简单依赖只有 ZooKeeper可选单机模式不需要配置熟练之后十分钟能起一个实例。它支持 TCP 直连客户端消费也支持写入 Kafka、RocketMQ这意味着上游可以统一用一个方案下游随便怎么接都行。另外Canal 在阿里巴巴内部已经有多年大规模使用经验binlog 解析的稳定性经过了验证。社区也比较活跃遇到问题基本都能搜到解决方案。相比自己基于 mysql-binlog-connector-java 从零写解析程序用 Canal 能把开发成本都省下来只需要专注做同步链路的对接。提示Canal 并不是唯一选择像 Maxwell、Debezium 也可以做类似的事情。选型时重点看团队语言栈、目标库类型、以及运维成本。如果你的目标主要是 Kafka Flink 这套生态Debezium 也值得对比如果主要还是在 MySQL 生态内同步Canal 上手更直接。2. MySQL 侧准备binlog 开关与账号权限Canal 的数据源是 MySQL 的 binlog所以第一步不是部署 Canal而是先把源库的 binlog 打开。这一步是整条链路的地基很多人部署完 Canal 发现收不到数据回头一查binlog 根本没开典型的返工。2.1 先确认源库当前状态登录 MySQL 检查三个东西是否开启了 binlog、binlog 格式、当前 server-id。SHOW VARIABLES LIKE log_bin; SHOW VARIABLES LIKE binlog_format; SHOW VARIABLES LIKE server_id; SHOW MASTER STATUS;如果 log_bin 是 OFF或者 binlog_format 不是 ROW就需要改配置。有一点要注意改完配置之后必须重启 MySQL 才能生效所以尽量安排在业务低峰期操作。如果 binlog 已经开启也要看 binlog_formatCanal 对 STATEMENT 格式支持有限个人建议直接切 ROW。2.2 修改 my.cnf 并理解关键参数以 Linux 环境为例通常修改 /etc/my.cnf 或 /etc/mysql/mysql.conf.d/mysqld.cnf在 [mysqld] 段落下加这几项[mysqld] server-id 100 log-bin mysql-bin binlog-format ROW binlog-row-image FULL expire_logs_days 7 max_binlog_size 512M其中 server-id 必须和同局域网内其他 MySQL 实例不同因为 Canal 伪装成从库后这个 server-id 会参与主从协议通信冲突会导致同步异常。log-bin 是 binlog 文件前缀实际生成的文件会是 mysql-bin.000001 这种形式。binlog-format 设置为 ROW 是 Canal 能正确解析行数据的关键。ROW 格式记录的是每一行数据的变化UPDATE 事件里包含修改前后的整行字段这样 Canal 才能把“哪个字段从什么值变成了什么值”完整解析出来。binlog-row-image 保持默认的 FULL确保镜像里包含所有列如果设置成 MINIMAL 的话有些版本下 Canal 拿不到完整的旧行数据容易出问题。expire_logs_days 是 binlog 清理周期。这个要重点关注如果清理太快Canal 长时间断连或者下游处理不过来再来消费时可能发现 binlog 已经被 purge只能从位点重新初始化。一般建议保留至少 7 天具体看同步链路的容错能力。2.3 创建 Canal 专用账号Canal 连接源库需要账号权限原则是最小权限不需要给 root。执行下面的 SQLCREATE USER canal% IDENTIFIED BY canal_pass; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO canal%; FLUSH PRIVILEGES;SELECT 权限用于在需要的时候查询表结构信息比如解析 Data 事件时要根据表结构反序列化数据。REPLICATION SLAVE 和 REPLICATION CLIENT 是建立复制协议和查询主库状态用的是 Canal 伪装成从库的必要权限。如果源库是 MySQL 8.0还需要注意密码插件问题。Canal 对 caching_sha2_password 的支持需要相应配置建议建号时直接用 mysql_native_passwordCREATE USER canal% IDENTIFIED WITH mysql_native_password BY canal_pass;我在 MySQL 8.0.30 上遇到过连接报错就是因为默认密码插件的问题换回 mysql_native_password 后连接就正常了。2.4 验证 binlog 是否就绪重启 MySQL 后重新执行一次SHOW VARIABLES LIKE log_bin; SHOW MASTER STATUS;能看到 log_bin 是 ON且有具体的 File 和 Position 输出就说明 binlog 已经开启了。这里输出的 File 名和 Position 值后面配置 Canal 实例时可以用到但更稳妥的做法是让 Canal 自动从当前位点开始消费人工指定位点容易因为理解偏差导致丢数据。注意如果 MySQL 启用了 GTID 模式源库配置里通常会有 gtid_modeON 和 enforce_gtid_consistencyON这种情况下 canal 的 instance 配置可以开启 gtid 支持避免位点漂移带来的定位困难。后面部署部分会再说。3. Canal Server 部署与实例配置环境准备好之后就可以部署 Canal 服务端。我用的是 canal-deployer 1.1.6 版本不同版本在目录结构和参数上略有差异建议以官方 release 为准但整体流程是通用的。3.1 下载与目录结构下载 canal.deployer-1.1.6.tar.gz解压到指定目录比如 /opt/canalwget https://github.com/alibaba/canal/releases/download/canal-deployer-1.1.6/canal.deployer-1.1.6.tar.gz mkdir -p /opt/canal tar -zxvf canal.deployer-1.1.6.tar.gz -C /opt/canal解压后的核心目录是 conf 和 lib。conf 里面有两个关键配置canal.properties 是服务端全局配置决定 Canal 实例如何对外提供服务conf/example/instance.properties 是单个实例的配置决定连接哪个 MySQL、监听哪些表。这个 example 就是默认实例名一个 Canal 服务端可以起多个实例不同实例可以对接不同的源库。3.2 修改 canal.propertiescanal.properties 里需要关注的参数主要是端口和服务模式。默认配置下Canal 对外提供 TCP 端口 11111客户端可以通过这个端口订阅数据。如果要对接 Kafka还需要把 serverMode 改为 kafka。canal.port 11111 canal.serverMode tcp canal.zkServers 单机测试时不需要 ZooKeeperzkServers 留空即可。生产环境下建议引入 ZooKeeper多个 Canal 实例做高可用时可以依赖 zk 管理元数据。再看实例部分的配置。canal.properties 里有几个针对实例的全局参数canal.instance.global.mode manager canal.instance.global.lazy false canal.instance.global.spring.xml classpath:spring/file-instance.xml这行 spring 配置决定了实例的创建方式默认是 file-instance.xml表示从 conf/example 目录加载 instance.properties。3.3 修改 instance.properties接着修改 conf/example/instance.properties这是最关键的一步。核心配置包括源库地址、账号、位点信息和过滤规则。canal.instance.master.address 127.0.0.1:3306 canal.instance.dbUsername canal canal.instance.dbPassword canal_pass canal.instance.connectionCharset UTF-8 canal.instance.tsdb.enable true canal.instance.filter.regex .*\\..*master.address 填源库地址和端口。dbUsername 和 dbPassword 填刚才创建的账号。connectionCharset 建议明确指定为 UTF-8Canal 在解析字符串类型数据时依赖这个配置填错了容易出现中文乱码。tsdb.enable 是 Canal 的表结构存储功能开启后会自动维护表结构元数据尤其在源表结构发生变化时能保证解析正确。filter.regex 是表过滤正则。默认的.*\..*表示监听所有库所有表。如果只想监听某个库的某些表可以这样写canal.instance.filter.regex test_db\\..*这里有一个容易踩的坑属性文件里反斜杠是转义符所以规则里表达库名和表名之间的点号时要写成\\.否则正则表达不对启动后你会发现某些表收不到数据。如果源库开了 GTID可以在 instance.properties 里加canal.instance.gtid.enable true开启后 Canal 会优先使用 GTID 定位位点比文件名加偏移量的方式更可靠尤其是在 MySQL 主从切换场景下GTID 能避免位点错乱。3.4 启动与日志观察配置完成后启动服务sh /opt/canal/bin/startup.sh启动后先看日志tail -f /opt/canal/logs/example/example.log看到start the canal client、register successfully这类日志基本说明实例启动成功。如果日志里出现connect failed或者Access denied优先检查数据库账号密码和权限。如果出现Cant find binlog说明配置的位点对应的 binlog 文件已经被清理需要重置位点。经验不要一上来就手工指定位点。新部署的 Canal 实例instance.properties 可以不配置 master.journal.name 和 master.positionCanal 会从当前时间点开始解析这样最简单不容易出错。如果业务要求从某个历史时间点开始同步就需要先确认对应 binlog 文件还存在再填位点。4. 同步链路建设目标库不同接入姿势也不同“其它库”这三个字意味着目标端的多样性。我在实际项目里用过三种接入方式分别对应三种典型场景这里展开讲清楚。4.1 方式一MySQL 到 MySQL用自带的 canal-adapter 落库如果你的需求是把一张业务表实时同步到另一个 MySQL 实例最简单的方式是用 Canal 自带的 adapter 组件。Canal 1.1.1 之后deployer 包内已经集成了 adapter可以直接加配置使用不需要额外部署一套服务。先在 canal.properties 里找到 adapter 相关配置确保canal.adapter.manager true canal.instance.global.spring.xml classpath:spring/default-instance.xml然后在 conf 目录下配置 adapter 的数据源和目标库连接。具体路径是 conf/canal-adapter/application.yml需要配置源库连接和 canal server 地址server: port: 8081 spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 canal.conf: canalServerHost: 127.0.0.1:11111 batchSize: 1000 syncBatchSize: 1000 retries: 0 timeout: 30000 mode: tcp srcDataSources: defaultDS: url: jdbc:mysql://127.0.0.1:3306/source_db?useUnicodetruecharacterEncodingUTF-8 username: canal password: canal_pass canalAdapters: - group: rdb groups: - type: rdb name: mysql driver: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/target_db?useUnicodetruecharacterEncodingUTF-8 username: root password: target_pwd这里 srcDataSources 配置的是源库canalAdapters 下面配置的是目标库。注意 adapter 分为 group 和 outerAdapter目标连接池写在 rdb 类型下。接着在 conf/canal-adapter/conf/rdb/ 目录下新建一个映射文件比如 user.yml用来告诉 adapter 哪些表要同步到目标库的哪张表dataSourceKey: defaultDS destination: example groupId: outerAdapterKey: mysql concurrent: true dbMapping: database: source_db table: t_user targetTable: t_user targetPk: id: id mapAll: true这个映射文件的含义是从 defaultDS 对应的库里读取 t_user 表的数据变更目标写到 target_db 下的 t_user 表主键是 id且把所有字段都映射过去。做完这两步重新启动 Canal 服务。启动完成后往 source_db.t_user 插入几条数据观察目标库的 t_user数据应该能比较快地出现。这里要注意canal-adapter 本身不会自动建表目标表必须提前建好否则同步的时候会报表不存在的错误。4.2 方式二MySQL 到 Kafka下游自己消费更多场景是目标端不是 MySQL而是 Kafka。Canal 把解析出来的 binlog 事件序列化后写入 Kafka下游无论是接 Flink 还是自己写消费者都很方便。启用 Kafka 模式只需要两步。第一步把 canal.properties 的 serverMode 改成 kafkacanal.serverMode kafka canal.mq.servers 127.0.0.1:9092 canal.mq.flatMessage truecanal.mq.servers 是 Kafka 的 broker 地址。canal.mq.flatMessage 建议设为 true这样会把 Canal 的 Entry 转成更易读的扁平 JSON消费者处理起来简单很多。如果设成 false发出去的是 protobuf 格式的原始 Entry消费方还得自己引入 Canal 的 protobuf 依赖做反序列化没必要。第二步在 instance.properties 里配置 topic 规则canal.mq.topic canal_topic canal.mq.dynamicTopic test_db\\..*canal.mq.topic 是全部事件默认写入的 topicdynamicTopic 可以按库表动态路由到不同 topic。比如想让 source_db 的所有表写到一个 topic其他表写到另一个 topic可以用这个配置。设置完成后重启 Canal然后写一个简单的消费者验证KafkaListener(topics canal_topic, groupId canal-consumer) public void handle(String msg) { System.out.println(msg); }插入数据后消费者能打印出类似这样的 JSON{data:[...],database:source_db,es:1728000000000,id:1,isDdl:false,old:null,pkNames:[id],sql:,table:t_user,ts:1728000000000,type:INSERT}看到这种消息说明链路已经通了。接下来下游怎么处理、怎么入库就是你们自己的业务逻辑了。4.3 方式三MySQL 到 ClickHouse 或 TDengine如果目标端是 ClickHouse 或者 TDengine我建议走 Kafka 消费端落库这条链路不要指望 canal-adapter 直接搞定。adapter 对 ClickHouse 的适配能力比较有限而且 ClickHouse 的实时写入对批次大小、去重策略都有要求自己写消费端反而更好控制。我之前做过一个 MySQL 到 TDengine 的同步任务思路就是把 Canal 写到 Kafka然后用一个 Java 消费者轮询消息把 JSON 里的 data 字段取出来组装成 TDengine 的 SQL 批量写入。TDengine 的建表逻辑需要提前准备好如果表结构经常变化可以在消费端做动态建表比用 adapter 灵活得多。Flink 是另一条更重的路线。如果数据量很大、下游要做窗口计算可以考虑直接在 Flink 里用 Flink CDC 读取 MySQL binlog跳过 Canal。但如果你已经有一套 Canal 基础设施用 Canal Kafka Flink 也很常见Canal 只负责把变更事件变成消息Flink 负责计算和落库职责上更解耦。经验目标库是列式存储时不要在同步链路里做逐行 INSERT。拉长批次、攒一批再批量写入性能能差出一个数量级。Canal 的 batchSize 和 syncBatchSize 就是干这个用的可以根据线上数据量调整到 500 到 2000 之间。4.4 位点管理与避免丢数据Canal 的消费位点维护在服务端的 meta.dat 文件里默认位置是 conf/example/meta.dat。这个文件记录的是当前消费到哪个 binlog 文件、哪个偏移量。正常情况下不需要人工处理但如果 Canal 异常退出重启后会从 meta.dat 记录的位点继续消费这保证了数据不会重复或丢失太多。不过要小心一个场景如果 source 库发生了主从切换binlog 文件的路径和序号可能发生变化meta.dat 里记录的位点可能失效。解决思路是开启 GTID或者在切换后重置位点。我当时协调 DBA 在源库开启了 GTID之后 Canal 那边基本不用再手工干涉。5. 核心原理拆解Canal 如何伪装成从库消费 binlog用熟练了之后还是要理解 Canal 的原理因为排障时最关键的就是“这条数据为什么没同步”。如果你不知道 binlog 是怎么被解析的遇到问题就只能瞎猜。5.1 MySQL 主从复制的执行过程正常情况下MySQL 主库把数据变更写入 binlog从库通过 IO 线程拉取 binlog 并写入自己的 relay log再由 SQL 线程回放。Canal 做的事情就是把“从库”这个角色替换成自己。Canal 客户端连接源库时会带上一个 server-id发送 COM_REGISTER_SLAVE 命令把自己注册成从库。注册成功之后就可以通过 COM_BINLOG_DUMP 命令向主库请求 binlog 数据。主库会把这个连接当成普通从库把 binlog 源源不断地推送过来。5.2 伪装从库后的解析链路Canal 拿到原始 binlog 字节流之后会经过几个核心环节LogFetcher负责从主库拉取 binlog 日志这个角色相当于 MySQL 从库的 IO 线程。LogParser解析 binlog 文件格式把二进制日志转换成 Canal 内部的事件结构。EventSink对解析出来的事件做过滤、去重等处理然后进入存储。EventStore负责把事件存储在内存或者持久化存储中供客户端拉取。MetaManager管理位点信息记录消费到哪个位置。整个链路每一步都有对应的日志输出。排查时如果数据没到下游先看是卡在 LogFetcher 拉取、LogParser 解析还是卡在 EventStore 分发。日志里经常能看到具体原因比如表结构获取失败、binlog 跨文件无法定位。canal 的 binlog 解析框架本身做了很多优化。比如解析 ROW 格式事件时会根据 MySQL 表结构把二进制字段值还原成字符串对于大字段也做了类型映射处理。理解这一点就知道为什么源库表结构发生变化时Canal 需要重新拉取表结构信息也就是前文提到的 tsdb 功能。5.3 ROW、STATEMENT、MIXED 三种 binlog 格式对 Canal 的影响binlog 一共有三种格式。STATEMENT 格式记录的是 SQL 语句本身比如UPDATE t_user SET status1 WHERE id123。MIXED 格式是前两者的混合根据语句类型自动选择。ROW 格式记录的是行的变更前后数据。Canal 只推荐使用 ROW 格式因为只有 ROW 格式能拿到完整的数据快照。STATEMENT 格式需要回放 SQL 才能知道结果不仅解析成本高有些非确定性函数还会导致主从数据不一致。Canal 对 STATEMENT 格式支持很差如果你的源库还在用 STATEMENT最好直接改掉。5.4 数据重复与顺序问题怎么理解Canal 向客户端投递事件的语义是 at-least-once也就是说在极端情况下客户端可能重复收到同一条变更事件。比如 Canal 处理完一批事件、更新位点之前进程宕机重启后会从旧位点重新解析导致同一批数据被再次投递。下游如果要严格精确必须自己保证幂等。最简单的做法是在目标表上加业务主键INSERT 写成 INSERT ... ON DUPLICATE KEY UPDATE或者消费时先按主键查一次再决定新增还是更新。如果你的目标端是 ClickHouse可以靠 ReplacingMergeTree 引擎去重。顺序问题也要注意。同一行的变更事件Canal 会按照 binlog 顺序顺序发出但如果你下游用了多个并发消费者不同消费者处理同一行的速度不同就可能出现后写的先落库、先写的后落库造成最终数据不一致。解决思路是按主键或表名做分区确保同一行的消息进同一个分区、同一个消费线程。6. 常见问题与排查实录这部分是把我在实际维护中遇到的高频问题整理成一份速查表每个问题都写了排查思路很多是文档里不会明确写的细节。现象可能原因排查/解决办法启动时报 Access denied数据库账号权限不足确认账号有 REPLICATION SLAVE、REPLICATION CLIENT、SELECT 权限连接超时日志里出现 Communications link failure源库防火墙/网络不通telnet 源库 3306 端口确认账号允许远程登录MySQL 8 连接报 SSL 相关错误账号密码插件或 SSL 配置不兼容建号时用 mysql_native_password或在 JDBC URL 里加 useSSLfalse实例启动成功但收不到变化binlog 格式不是 ROW确认 binlog_formatROW改完重启 MySQL某些表收不到数据过滤正则写错检查 instance.properties 的 filter.regex注意转义点号一段时间断连后报 Cant find binlogbinlog 过期被清理确认 expire_logs_days/expire_logs_seconds必要时手工重置位点中文数据乱码connectionCharset 配置错误明确配置为 UTF-8并确认 JVM 默认编码同步到目标库时间少了 8 小时时区处理不一致目标库连接 URL 加 serverTimezoneAsia/Shanghaiadapter 的 jackson time-zone 设为 GMT8重复消费at-least-once 语义下游做幂等按主键 upsert数据顺序错乱并发消费导致按主键哈希分流到固定消费线程或降低并发度6.1 实例启动成功但一直收不到数据这个问题最隐蔽也最常出现。有一次我配置完之后日志没有任何异常往源库插入数据消费者那边就是没有消息。排查了半个小时最后发现 instance.properties 里的 filter.regex 写成了test_db.*本来想匹配所有表但属性文件里没有对点号做转义实际匹配不到任何表。正确的写法是test_db\\..*。这个坑几乎每个初用 Canal 的人都会踩包括我自己。6.2 MySQL 8 的 SSL 连接配置不兼容源库是 MySQL 8.0 时Canal 连接经常报类似Access denied或者 SSL 握手失败的错误。原因一部分是默认账号密码插件是 caching_sha2_password一部分是 JDBC 驱动默认开启 SSL。我的做法是创建 Canal 账号时指定 mysql_native_password同时给 Canal 的 JVM 加上连接参数在 canal.properties 里配置canal.instance.master.jdbc.driver com.mysql.cj.jdbc.Driver canal.instance.master.jdbc.url jdbc:mysql://127.0.0.1:3306?useSSLfalseallowPublicKeyRetrievaltrueallowPublicKeyRetrieval 这个参数在 MySQL 8 下经常需要显式开启不然客户端在获取公钥时会报错。如果你的版本不需要它也可以不加。6.3 位点过期导致无法增量同步还有一次是 Canal 服务因为服务器维护停机了五天等启动时直接报错说找不到某个 binlog 文件。查了下源库配置expire_logs_days 设置的 2 天binlog 已经被清理旧位点自然失效。这种情况只能重新初始化位点。最简单的办法是删除 conf/example/meta.dat然后重启 Canal让它从当前时间开始消费。但要注意这样会丢失停机期间的数据。如果需要补数据就得先做一次全量同步再启动增量才能保证两边数据一致。经验Canal 并非只能做增量。全量 增量是生产环境常见的组合先写一个数据迁移任务把存量数据搬过去再开启 Canal 增量同步两者配合才能保证最终一致。不要期望 Canal 帮你补全量它只管新增变化。6.4 时间字段差 8 小时同步到目标库发现时间字段差了 8 小时通常是 JDBC 连接串里没指定 serverTimezone默认走了 UTC。处理办法是在所有 MySQL 目标连接和源连接 URL 里明确加serverTimezoneAsia/Shanghai同时 adapter 的 jackson time-zone 配成GMT8。Canal 解析出来的 es 和 ts 字段是毫秒时间戳本身不包含时区概念最终显示成什么时间取决于下游格式化时用的时区。所以这个问题并不仅仅是 Canal 配置的问题下游消费逻辑也要注意。6.5 同步性能优化数据量大之后Canal 本身的吞吐也会成为瓶颈。我当时做了一轮调优效果比较明显的几个参数canal.instance.filter.black.regex canal.properties: canal.instance.binlog.semantic false canal.instance.batch.size 5000 canal.instance.memory.buffer.size 16384 canal.instance.memory.buffer.memunit 1024batch.size 决定一次从 binlog 里读取多少条事件内存 buffer 决定攒多少数据再投递给客户端。调大这些值可以减少网络交互次数提升吞吐但会占用更多内存具体数值要看服务器配置。客户端拉取时也可以把 batchSize 调大一次拉个几百上千条处理比一条条处理快多了。生产环境建议把 Canal 单独部署在一台机器上不要和业务服务混布。Canal 对 CPU 和内存的占用不算低尤其是解析大事务的时候容易把业务服务的资源抢走。7. 这套链路后续还能怎么扩展同步链路跑起来之后你会发现剩下的工作基本都围绕两点稳定性和扩展性。从稳定性角度看建议把 Canal 纳入监控。日志里一旦出现 error 级别输出就该触发告警。位点进度也可以做成指标比如通过 Canal 的 metrics 接口拉取当前位置和数据库当前 binlog 位点做差值就能知道同步延迟了多少。延迟超过阈值就告警比被动等业务反馈要省心得多。从扩展性角度看如果下游要接多个系统就不要让每个下游都直连 Canal。统一推送到 Kafka让各下游按需订阅这个架构后期扩展起来最舒服。新增一个数据消费者只需要新写一组消费逻辑不用碰 Canal 配置。最后分享一个个人习惯每次改 Canal 配置之前我都会先把 conf 目录整体备份一份尤其是 instance.properties 和 application.yml。Canal 的配置文件是纯文本改错了影响的是整个同步链路回滚比重新配置快得多。这点习惯帮我避过好几次线上事故你也可以试试。
返回列表