编辑
2026-10-23
canal
00

目录

Canal + Elasticsearch 实时同步实战(一):选型、架构与前置准备
目录
一、为什么要用 Canal
二、整体架构
一个容易被忽略的关键认知
三、环境与版本矩阵
版本兼容性,请看这张表再动手
四、MySQL 前置准备
4.1 开启 binlog(ROW 模式)
4.2 创建同步账号
4.3 验证权限
4.4 MySQL 8.4 的认证变化(提前预警)
五、安装 JDK 8
为什么要建软链
六、本篇小结

Canal + Elasticsearch 实时同步实战(一):选型、架构与前置准备

系列文章

  • 第一篇:选型、架构与前置准备 ← 本篇
  • 第二篇:部署 canal-server 与 adapter,跑通全量 + 增量
  • 第三篇:8 个踩坑记录与生产环境注意事项

环境:CentOS 7.9 + MySQL 8.4.1 + Elasticsearch 7.9.3 + Canal 1.1.7

如果你正打算做 MySQL → ES 的实时同步,建议三篇按顺序看。第三篇的坑,基本都是第二篇部署时踩出来的。


目录


一、为什么要用 Canal

业务库在 MySQL,但搜索、列表页、运营后台需要走 Elasticsearch。常见做法有三种:

方案实时性侵入性说明
业务代码双写高高每个写库的地方都要写 ES,容易漏、容易不一致
定时全量刷低低数据量大时扛不住,延迟以分钟/小时计
解析 binlog准实时(秒级)零侵入Canal 就是这一类

Canal 的原理很朴素:把自己伪装成 MySQL 的从库(slave),订阅 binlog,把变更解析成结构化消息。

业务代码一行不用改,MySQL 侧只需要保证 binlog_format=ROW。这是它相比"代码双写"最大的优势——存量代码零改造,也不会因为某处漏写导致数据不一致。

代价是:你多了一个需要运维的中间件,而且它的版本兼容性对 MySQL 版本很敏感(这一点第三篇会详细展开)。


二、整体架构

┌──────────────┐ binlog(ROW) ┌────────────────┐ TCP 11111 ┌─────────────────┐ REST 9200 ┌──────────┐ │ MySQL │ ──────────────> │ canal-server │ ────────────> │ canal-adapter │ ────────────> │ ES │ │ (业务库) │ │ (deployer) │ │ (es7 plugin) │ │ │ └──────────────┘ └────────────────┘ └─────────────────┘ └──────────┘ │ │ │ 开 binlog ROW 解析 binlog 为 按映射 SQL 查询 + 写入 建复制账号 Entry(行级变更) (JDBC 读取 → ES Bulk)

三个组件的职责要分清,这是理解后面所有配置的关键:

canal-server(deployer) 只管"从 MySQL 捞 binlog 并解析",它完全不知道 ES 的存在。对外暴露 TCP 11111,客户端订阅后能收到 Entry(一行数据的增删改)。它也不做数据加工,binlog 里是什么就吐什么。

canal-adapter 一个标准 Spring Boot 应用,作为 canal-server 的客户端。订阅到变更后,根据你写的映射文件,去 MySQL 用 JDBC 查出完整数据行,再写入 ES。

所以它需要两份连接信息:

  1. canal-server 的地址(127.0.0.1:11111)
  2. MySQL 的 JDBC 连接(用于回查数据)

Elasticsearch 最终存储。

一个容易被忽略的关键认知

adapter 不是把 binlog 里的字段直接塞进 ES,而是拿到主键后重新查一次数据库。

这个设计带来两个后果:

  • 好处:映射 SQL 可以写任意 join,甚至可以做字段拼接、类型转换。binlog 里只有变更的列,但你可以查出整行甚至关联表的数据。
  • 约束:UPDATE / DELETE 事件必须能通过 _id 定位到文档。所以映射里的 _id 必须是主键或能唯一定位一行,否则更新会变成新增、删除会失效。

理解了这一点,后面看到"为什么配置里要写 SQL"就不会觉得突兀了。


三、环境与版本矩阵

组件版本部署方式备注
JDK1.8.0_202二进制包Canal 1.1.7 只支持 JDK 8
MySQL8.4.1Docker注意,8.4 有兼容性坑
Elasticsearch7.9.3Docker已开 xpack security
canal-server1.1.7tar 包
canal-adapter1.1.7tar 包

版本兼容性,请看这张表再动手

MySQL 版本Canal 1.1.7 支持情况建议
5.7✅ 完美直接用
8.0✅ 支持推荐组合
8.4⚠️ 有坑(SHOW MASTER STATUS 被移除)能跑,但要绕过
9.x❌ 不建议mysql_native_password 已被彻底移除

如果条件允许,MySQL 选 8.0。 本文因为业务库已经是 8.4,所以走的是绕过方案,具体做法在第二篇,坑的来龙去脉在第三篇。

Canal 1.1.7 发布于 2022 年,MySQL 8.4 发布于 2024 年,两者之间发生了三处破坏性变更: SHOW MASTER STATUS 移除、mysql_native_password 默认关闭、JDBC 驱动认证方式升级。 这不是 Canal 的锅,但踩坑的成本确实由使用者承担。


四、MySQL 前置准备

4.1 开启 binlog(ROW 模式)

先查现状:

sql
SELECT @@version, @@log_bin, @@binlog_format, @@binlog_row_image, @@server_id;

必须满足:

参数要求值不满足时怎么改
log_bin1my.cnf 加 log-bin=mysql-bin,重启生效
binlog_formatROW加 binlog_format=ROW,需要重启
binlog_row_imageFULL加 binlog_row_image=FULL
server_id≠ 0加 server_id=1(任意非 0、且不与其他节点冲突的值)

为什么 binlog_row_image 必须是 FULL?

如果设成 MINIMAL,binlog 里只记录被修改的列,不记录整行。Canal 解析 UPDATE 事件时拿不到完整的前后镜像,做数据同步就会缺字段、算错值。这是很多人同步后"部分字段莫名变 null"的根因。

4.2 创建同步账号

MySQL 8.0:

sql
CREATE USER 'canal'@'%' IDENTIFIED WITH mysql_native_password BY '<CANAL_DB_PASSWORD>'; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; FLUSH PRIVILEGES;

MySQL 5.7:

sql
CREATE USER 'canal'@'%' IDENTIFIED BY '<CANAL_DB_PASSWORD>'; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; FLUSH PRIVILEGES;

三个权限缺一不可:

权限用途
REPLICATION SLAVE订阅 binlog 的核心权限,没有它 dump 建立不起来
REPLICATION CLIENT执行 SHOW MASTER STATUS 查位点
SELECTadapter 回查数据要用

生产环境建议把 '%' 收紧成 Canal 服务器的固定 IP,比如 'canal'@'10.0.0.5'。

4.3 验证权限

sql
SHOW GRANTS FOR 'canal'@'%'; SELECT user, host, plugin FROM mysql.user WHERE user = 'canal';

plugin 那一列就是认证方式,决定了你能不能直接用 Canal 默认配置连上——这一点很关键,往下看。

4.4 MySQL 8.4 的认证变化(提前预警)

MySQL 8.4 里 mysql_native_password 默认关闭,而网上 90% 的 Canal 教程都让你用这个插件,于是会撞上:

ERROR 1524 (HY000): Plugin 'mysql_native_password' is not loaded

更麻烦的是,SET PERSIST mysql_native_password = ON; 会报 Unknown system variable——这个参数在 8.4.1 里根本不是系统变量,只能改配置文件重启:

bash
docker exec <容器名> sh -c "sed -i '/^\[mysqld\]/a mysql_native_password=ON' /etc/my.cnf" docker restart <容器名>

但本文最终没有走这条路,而是直接用默认的 caching_sha2_password:

sql
CREATE USER 'canal'@'%' IDENTIFIED BY '<CANAL_DB_PASSWORD>';

原因很简单:改 MySQL 的认证插件影响面太大(其他客户端也可能依赖),而换一个 JDBC 驱动是只影响 Canal 一件事的低风险操作。

代价是 adapter 自带的驱动太老,必须手动替换——具体操作在第二篇,原理在第三篇的坑 2。

如果你确认可以改 MySQL 配置,那么开 mysql_native_password 是最省事的路,Canal 侧完全不用改。


五、安装 JDK 8

Canal 1.1.7 只能跑在 JDK 8 上(JDK 11+ 会有反射相关的兼容问题)。

CentOS 7 已经 EOL,yum install java-1.8.0-openjdk 大概率 404、Cannot find a valid baseurl。别折腾 yum 源了,直接下二进制包:

bash
mkdir -p /usr/local/java tar -xzf jdk-8u202-linux-x64.tar.gz -C /usr/local/java # 全局环境变量(登录 shell 生效) cat > /etc/profile.d/jdk.sh <<'EOF' export JAVA_HOME=/usr/local/java/jdk1.8.0_202 export PATH=$JAVA_HOME/bin:$PATH EOF # 非登录 shell(systemd 启动的服务)也能找到 java ln -sf /usr/local/java/jdk1.8.0_202/bin/java /usr/local/bin/java java -version

输出应为:

java version "1.8.0_202" Java(TM) SE Runtime Environment (build 1.8.0_202-b08) Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)

为什么要建软链

Canal 的 startup.sh 判断逻辑是:JAVA_HOME 为空就用 PATH 里的 java。

而 systemd 启动的服务不加载 /etc/profile.d/,JAVA_HOME 拿不到。这时候要么在 unit 文件里显式声明 Environment,要么让 PATH 里有 java。软链是兜底,两者都做最稳。

第二篇配 systemd 时会把 JAVA_HOME 写进 unit,这里先留个伏笔。


六、本篇小结

到这里,准备工作完成了,应该具备:

  • ✅ MySQL 开启了 ROW 格式 binlog
  • ✅ 建好了带三个权限的 canal 账号
  • ✅ 知道了当前 MySQL 版本的认证插件(决定了第二篇要不要换驱动)
  • ✅ JDK 8 装好,java -version 正常

下一步:下载 Canal 的 1.1.7 安装包(canal.deployer-1.1.7.tar.gz 和 canal.adapter-1.1.7.tar.gz),然后进入第二篇开始部署。

提醒:两个包加起来约 370MB,adapter 包偏大(270MB)是因为打包了所有数据库适配器的驱动。国内从 GitHub Release 下载可能很慢,可以先下到本地再传服务器。


下一篇:部署 canal-server 与 adapter,跑通全量 + 增量

涉及:目录规划、instance.properties 逐项说明、位点管理、驱动替换、application.yml 的四个关键配置、ES 索引 mapping、ETL 全量接口、systemd 托管。

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:章鱼哥

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!