系列文章
- 第一篇:选型、架构与前置准备 ← 本篇
- 第二篇:部署 canal-server 与 adapter,跑通全量 + 增量
- 第三篇:8 个踩坑记录与生产环境注意事项
环境:CentOS 7.9 + MySQL 8.4.1 + Elasticsearch 7.9.3 + Canal 1.1.7
如果你正打算做 MySQL → ES 的实时同步,建议三篇按顺序看。第三篇的坑,基本都是第二篇部署时踩出来的。
业务库在 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。
所以它需要两份连接信息:
127.0.0.1:11111)Elasticsearch 最终存储。
adapter 不是把 binlog 里的字段直接塞进 ES,而是拿到主键后重新查一次数据库。
这个设计带来两个后果:
_id 定位到文档。所以映射里的 _id 必须是主键或能唯一定位一行,否则更新会变成新增、删除会失效。理解了这一点,后面看到"为什么配置里要写 SQL"就不会觉得突兀了。
| 组件 | 版本 | 部署方式 | 备注 |
|---|---|---|---|
| JDK | 1.8.0_202 | 二进制包 | Canal 1.1.7 只支持 JDK 8 |
| MySQL | 8.4.1 | Docker | 注意,8.4 有兼容性坑 |
| Elasticsearch | 7.9.3 | Docker | 已开 xpack security |
| canal-server | 1.1.7 | tar 包 | |
| canal-adapter | 1.1.7 | tar 包 |
| 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 的锅,但踩坑的成本确实由使用者承担。
先查现状:
sqlSELECT @@version, @@log_bin, @@binlog_format, @@binlog_row_image, @@server_id;
必须满足:
| 参数 | 要求值 | 不满足时怎么改 |
|---|---|---|
log_bin | 1 | my.cnf 加 log-bin=mysql-bin,重启生效 |
binlog_format | ROW | 加 binlog_format=ROW,需要重启 |
binlog_row_image | FULL | 加 binlog_row_image=FULL |
server_id | ≠ 0 | 加 server_id=1(任意非 0、且不与其他节点冲突的值) |
为什么 binlog_row_image 必须是 FULL?
如果设成 MINIMAL,binlog 里只记录被修改的列,不记录整行。Canal 解析 UPDATE 事件时拿不到完整的前后镜像,做数据同步就会缺字段、算错值。这是很多人同步后"部分字段莫名变 null"的根因。
MySQL 8.0:
sqlCREATE 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:
sqlCREATE 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 查位点 |
SELECT | adapter 回查数据要用 |
生产环境建议把
'%'收紧成 Canal 服务器的固定 IP,比如'canal'@'10.0.0.5'。
sqlSHOW GRANTS FOR 'canal'@'%';
SELECT user, host, plugin FROM mysql.user WHERE user = 'canal';
plugin 那一列就是认证方式,决定了你能不能直接用 Canal 默认配置连上——这一点很关键,往下看。
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 里根本不是系统变量,只能改配置文件重启:
bashdocker exec <容器名> sh -c "sed -i '/^\[mysqld\]/a mysql_native_password=ON' /etc/my.cnf"
docker restart <容器名>
但本文最终没有走这条路,而是直接用默认的 caching_sha2_password:
sqlCREATE USER 'canal'@'%' IDENTIFIED BY '<CANAL_DB_PASSWORD>';
原因很简单:改 MySQL 的认证插件影响面太大(其他客户端也可能依赖),而换一个 JDBC 驱动是只影响 Canal 一件事的低风险操作。
代价是 adapter 自带的驱动太老,必须手动替换——具体操作在第二篇,原理在第三篇的坑 2。
如果你确认可以改 MySQL 配置,那么开
mysql_native_password是最省事的路,Canal 侧完全不用改。
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 源了,直接下二进制包:
bashmkdir -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,这里先留个伏笔。
到这里,准备工作完成了,应该具备:
canal 账号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 托管。


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