canal是阿里开源的数据同步工具,基于binlog可以将数据库同步到其他各类数据库中,目标数据库支持mysql,postgresql,oracle,redis,MQ,ES等。
一、 基本原理
1、主从复制原理
MySQL的主从复制是依赖于binlog,也就是记录MySQL上所有操作的逻辑日志保存在磁盘上。主从复制就是将binlog中的数据从主库传输到从库上,一般这个过程是异步的。
详细流程:
- 主库写binlog:主库的更新SQL(create、update、insert、delete等)被写入到binlog;
- 主库发送binlog:主库创建一个log dump线程来发送binlog给从库;
- 从库写relay log:(从库在连接到主节点时会创建一个IO线程,以请求主库更新的binlog),并且把binlog信息写入一个叫做relay log的日志文件;
- 从库回放:从库还会创建一个SQL线程读取relay log中的内容,并且在从库中回访,最终实现主从一致性。
2、Canal介绍
主要流程:
- Canal 服务端向 MySQL 的 master 节点传输dump协议;
- MySQL 的 master 节点接收到 dump 请求后推送 binlog 日志给 Canal 服务端,解析 binlog 对象(原始为byte流)转成 Json 格式;
- Canal 客户端通过 TCP 协议或 MQ 形式监听 Canal 服务端,同步数据到ES。
Canal架构
一个 server 代表一个 canal 运行实例,对应于一个 jvm,一个 instance 对应一个数据队列。
instance模块:
eventParser :数据源接入,模拟 slave 协议和 master 进行交互,协议解析;
eventSink :Parser 和 Store 链接器,进行数据过滤、加工、分发的工作;
eventStore :数据存储;
metaManager :增量订阅&消费信息管理器;
instance 是 canal 数据同步的核心,在一个 canal 实例中只有启动 instace 才能进行数据的同步任务。一个 canal server 实例中可以创建多个 Canal Instance 实例。每一个 Canal Instance 可以看成是对应一个 MySQL 实例。
Canal-HA机制
所谓 HA 即高可用,是 High Available 的简称。通常我们一个服务要支持高可用都需要借助于第三方的分布式同步协调服务,最常用的是zookeeper 。canal 实现高可用,也是依赖了zookeeper 的几个特性:watcher 和 EPHEMERAL 节点。
canal 的高可用分为两部分:canal server 和 canal client
canal server: 为了减少对 mysql dump 的请求,不同 server 上的 instance(不同 server 上的相同 instance)要求同一时间只能有一个处于 running,其他的处于 standby 状态,也就是说,只会有一个 canal server 的 instance 处于 active 状态,但是当这个 instance down 掉后会重新选出一个 canal server。
canal client: 为了保证有序性,一份 instance 同一时间只能由一个 canal client 进行 get/ack/rollback 操作,否则客户端接收无法保证有序。
server ha 的架构图如下:
大致步骤:
canal server 要启动某个 canal instance 时都先向 zookeeper 进行一次尝试启动判断(实现:创建 EPHEMERAL 节点,谁创建成功就允许谁启动);
创建 zookeeper 节点成功后,对应的 canal server 就启动对应的 canal instance,没有创建成功的 canal instance 就会处于 standby 状态。
一旦 zookeeper 发现 canal server A 创建的 instance 节点消失后,立即通知其他的 canal server 再次进行步骤1的操作,重新选出一个 canal server 启动 instance。
canal client 每次进行 connect 时,会首先向 zookeeper 询问当前是谁启动了canal instance,然后和其建立链接,一旦链接不可用,会重新尝试 connect。
Canal Client 的方式和 canal server 方式类似,也是利用 zookeeper 的抢占 EPHEMERAL 节点的方式进行控制。
应用场景
同步缓存 Redis /全文搜索 ES
当数据库变更后通过 binlog 进行缓存/ES的增量更新。当缓存/ES更新出现问题时,应该回退 binlog 到过去某个位置进行重新同步,并提供全量刷新缓存/ES的方法。
下发任务
当数据变更时需要通知其他依赖系统。其原理是任务系统监听数据库变更,然后将变更的数据写入 MQ/kafka 进行任务下发,比如商品数据变更后需要通知商品详情页、列表页、搜索页等相关系统。
这种方式可以保证数据下发的精确性,通过 MQ 发送消息通知变更缓存是无法做到这一点的,而且业务系统中不会散落着各种下发 MQ 的代码,从而实现了下发归集。
数据异构
在大型网站架构中,DB都会采用分库分表来解决容量和性能问题。但分库分表之后带来的新问题,比如不同维度的查询或者聚合查询,此时就会非常棘手。一般我们会通过数据异构机制来解决此问题。
所谓的数据异构,那就是将需要 join 查询的多表按照某一个维度又聚合在一个 DB 中让你去查询,canal 就是实现数据异构的手段之一。
二、实操
将 MySQL 中 coding 数据库的 article 表同步到 ES ”为需求举例
1、软件下载
电脑:Windows 11 JDK版本:jdk 11.0.21
1.1 MySQL(默认MySQL都已经安装了,登录 mysql 按如下执行)
- mysql 输入
show variables like 'log_bin;'
查看是否开启binlog,binlog显示为ON 为开启 - 创建一个 canal 用户,专门用于 canal 同步数据库使用
# 注意大小写
CREATE USER canal IDENTIFIED BY 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;
开启 binlog
首先在 mysql 的配置文件目录中查找配置文件 my.cnf(Linux环境)
vim my.cnf
server-id=1#master端的ID号【必须是唯一的】;log_bin=mysql-bin #同步的日志路径,一定注意这个目录要是mysql有权限写入的binlog-format=row #行级,记录每次操作后每行记录的变化。binlog-do-db=cheetah #指定库,缩小监控的范围。
重启 mysql:service mysqld restart,会发现在 /var/lib/mysql 下会生成两个文件 mysql-bin.000001 和 mysql-bin.index,当 mysql 重启或到达单个文件大小的阈值时,新生一个文件,按顺序编号 mysql-bin.000002,以此类推。
扩展
binlog 日志有三种格式,可以通过 binlog_format 参数指定。
statement
记录的内容是 SQL语句 原文,比如执行一条 update T set update_time=now() where id=1,记录的内容如下
同步数据时,会执行记录的 SQL 语句,但是有个问题,update_time=now() 这里会获取当前系统时间,直接执行会导致与原库的数据不一致。
row
为了解决上述问题,我们需要指定为 row,记录的内容不再是简单的 SQL 语句了,还包含操作的具体数据,记录内容如下。
ow 格式记录的内容看不到详细信息,要通过 mysql binlog 工具解析出来。
update_time=now() 变成了具体的时间 update_time=1627112756247,条件后面的 @1、@2、@3 都是该行数据第1个~3个字段的原始值(假设这张表只有3个字段)。
这样就能保证同步数据的一致性,通常情况下都是指定为 row,这样可以为数据库的恢复与同步带来更好的可靠性。
缺点:占空间、恢复与同步时消耗更多的IO资源,影响执行速度。
mixed
MySQL 会判断这条 SQL 语句是否可能引起数据不一致,如果是,就用 row 格式,否则就用 statement 格式。
1.2 ES
1、点击下载
2、将下载后的压缩包解压到一文件夹(路径最好不要含有中文或空格)
3、双击bin/elasticsearch.bat启动
4、检查是否成功,输入http://localhost:9200/?pretty
1.3 Kibana
1、安装地址:https://www.elastic.co/cn/downloads/past-releases#kibana
2、版本:7.14.0
3、解压安装到指定文件夹
4、双击 D:\ProgramFiles\kibana-7.14.0-windows-x86_64\bin\kibana.bat 启动
5、输入 http://localhost:5601 检验是否启动成功
6、在 Kibana 中创建ES索引(可以通过PostMan创建,看下图示意)
PUT article
{
"mappings" : {
"properties" :{
"id" :{
"type" : "integer"
},
"user_id" : {
"type" : "integer"
},
"article_type" : {
"type" : "integer"
},
"title" : {
"type" : "text",
"analyzer" : "ik_max_word"
},
"short_title" : {
"type" : "text",
"analyzer" : "ik_max_word"
},
"picture" : {
"type" : "text",
"analyzer" : "ik_max_word"
},
"summary" : {
"type" : "text",
"analyzer" : "ik_max_word"
},
"category_id" : {
"type" : "integer"
},
"source" : {
"type" : "integer"
},
"source_url" : {
"type" : "text",
"analyzer" : "ik_max_word"
},
"offical_stat" : {
"type" : "integer"
},
"topping_stat" : {
"type" : "integer"
},
"cream_stat" : {
"type" : "integer"
},
"status" : {
"type" : "integer"
},
"deleted" : {
"type" : "integer"
},
"create_time" : {
"type" : "date"
},
"update_time" : {
"type" : "date"
}
}
}
}
PostMan执行成功后返回200。
1.4 ik
1、下载地址:https://gitcode.com/medcl/elasticsearch-analysis-ik/tags/v7.17.2?utm_source=csdn_github_accelerator&isLogin=1
2、版本:7.17.2 (ik的版本必须与ES版本一致)
3、在 ES 目录下的 plugins 中创建一个文件夹ik(ES的安装目录最好不要有空格就是因为我安装的时候如果有空格 ik 启动的时候会报错)
4、将 ik 压缩包解压到 plugins/ik 目录下,解压后的目录结构如下
5、重启elasticsearch、kibana
6、访问 http://localhost:5601/app/dev_tools#/console,输入方框中的内容,点击运行,出现右侧的结果,就是说明安装成功了。
1.5 Canal
下载配置Canal的时候真的遇到了很多乱七八糟奇奇怪怪的问题,我的建议是面向百度编程搜索无解的时候不如静下来分析一下日志。
1、下载地址:https://github.com/alibaba/canal/releases
2、版本:1.1.7
先把框中这三个压缩包下载下来解压到文件夹:比如
canal.deployer:相当于canal的服务端,启动它才可以在客户端接收数据库变更信息
canal.adapter:增加客户端数据落地的适配以及启动功能(当 deployer 收到消息后,会根据不同的目录源做适配,比如 es 目标源适配等。看需求,目标源是Redis就不用这个。)
2、canal 配置
2.1 canal.deployer 配置
修改 canal.deployer-1.1.7\conf\example\instance.properties 文件
2.2 canal.deployer 启动
1、双击 “D:\ProgramFiles\canal.deployer-1.1.7\bin\startup.bat” 启动
2、在 “D:\ProgramFiles\canal.deployer-1.1.7\logs\canal\canal.log” 中查看启动日志,存在类似于下图的字样说明启动成功,也可能会在日志中看到binlog转成Json后的sql逻辑
3、如果之前同步过,想要重新做全量同步,那么需要删除 “D:\ProgramFiles\canal.deployer-1.1.7\conf\example\meta.dat” 文件,这个文件记录上次同步的时间和 binlog 位置。
2.3 source源码配置
我上边的 canal-canal-1.1.7 文件夹就是 source 解压之后
1、用 IDEA 打开 canal-canal-1.1.7 项目,等待依赖加载完毕,编译成功
2、在 “canal-canal-1.1.7\client-adapter\es7x\pom.xml” 中增加依赖
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>client-adapter.common</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
3、在 “canal-canal-1.1.7\pom.xml” 中增加如下片段,主要是打包跳过测试,避免报错
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire.version}</version>
<configuration>
<useSystemClassLoader>true</useSystemClassLoader>
<forkMode>once</forkMode>
<argLine>${argline} ${jacocoArgLine}</argLine>
<systemProperties>
<!-- common shared -->
</systemProperties>
<skipTests>true</skipTests> <!--默认关掉单元测试 -->
</configuration>
</plugin>
4、在maven插件中重新打包
5、打包成功后将打包后的产物 "D:\ProgramFiles\canal-canal-1.1.7\client-adapter\es7x\target\client-adapter.es7x-1.1.7-jar-with-dependencies.jar" 替换 "D:\ProgramFiles\canal.adapter-1.1.7\plugin\client-adapter.es7x-1.1.7-jar-with-dependencies.jar" (注意是把 source 中的产物替换到 adapter 文件夹下)
2.4 canal.adapter配置
1、先将 “D:\ProgramFiles\canal.adapter-1.1.7\conf\bootstrap.yml” 文件中的全部内容注释(这个文件应该是给 canal.admin 用的,不注释掉的话启动时会提供XX表不存在)
2、修改 “D:\ProgramFiles\canal.adapter-1.1.7\conf\application.yml” 文件,重点看一下方框里的内容就是,其他默认的不用改
3、在 “D:\ProgramFiles\canal.adapter-1.1.7\conf\es7” 文件夹创建 article.yml 文件
文件内容如下:
datasourceKey: defaultDS #源数据源的key,对应上面配置的srcDatasources中的值
destination: example #canal的instance或者MO的topic
groupId: g1 #对应MQ模式下的groupId,只会同步对应groupId的数据
esMapping:
_index: article #es 的索引名称
_id: _id #es 的 id,如果不配置该项必须配置下面的pk项 id则会由es自动分配
sql: "SELECT t.id As _id, t.id, t.user_id, t.article_type, t.title,t.short_title,
t.picture, t.summary, t.category_id, t.source, t.source_url, t.offical_stat,t.topping_stat,
t.cream_stat, t.status, t.deleted, t.create_time,
t.update_time FROM article t" # sq1映射
commitBatch: 1 # 提交批大小
“D:\ProgramFiles\canal.adapter-1.1.7\conf\es7” 解压后这个文件夹下应该会有3个默认的 yml 文件,最好删掉,不然启动的时候会报错
2.5 canal.adapter 启动
1、双击 “D:\ProgramFiles\canal.adapter-1.1.7\bin\startup.bat” 启动
2、到 “D:\ProgramFiles\canal.adapter-1.1.7\logs\adapter\adapter.log” 中查看 日志,有这样的显示说明启动成功,并且执行同步操作了
3、日志的最后能看到同步的条数和时间
4、也可以通过 curl 进行全量同步操作
curl http://127.0.0.1:8081/etl/es7/article.yml -X POST
5、在 Kibana 中查看,成功
三、Q&A
1、好多教程里没有 source 的部分,启动adapter的就会出现问题,先把source 的部分配置好再试下;
2、source源码在IDEA中编译一直失败
可能是这个地方没有改成你自己的仓库,也可能就是网络不好。
3、启动adapter时报错:Failed to bind properties under ‘es-mapping’ to com.alibaba.otter.canal.client.adapter.es.core.config.ESSyncConfig$ESMapping
用记事本打开 startup.bat 文件,classPath 分号前后的内容顺序换一下,下面是改好后的样子