canal实时增量同步mysql数据到elasticsearch

2,243 阅读3分钟

0. 原理

canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送 dump 协议
MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
canal 解析 binary log 对象(原始为 byte 流)

1. docker部署相关容器

elasticsearch、mysql、canal-server、kibana

注意elasticsearch的数据卷参数、elasticsearch和kibana的网络参数

参考之前文章:juejin.cn/post/684490…

2. 创建用户,不要使用mysql默认的root

CREATE USER canal IDENTIFIED BY 'canal'; 

GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';

FLUSH PRIVILEGES;

3. mysql elasticsearch canal-server应该都在一个网络中

在上面的文章中elasticsearch和kibana已经在同一个网络中,将mysql和canal-server也加入进去

## mysql
sudo docker run -d --name mysql -v /home/ubuntu/docker/mysql/volume:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 --network=elasticsearch-kibana mysql

## canal-server,注意用户名和密码是刚刚创建的canal/canal
sudo docker run -e canal.instance.master.address=192.168.0.106:3306 \
                    -e canal.instance.dbUsername=canal \
                    -e canal.instance.dbPassword=canal \
                    -e canal.instance.connectionCharset=UTF-8 \
                    -e canal.instance.tsdb.enable=true \
                    -e canal.instance.gtidon=false \
                    -e canal.instance.filter.regex=.*\\..*  -d -p 11111:11111 --name canal-server --network=elasticsearch-kibana canal/canal-server

inspect命令可以看到已经加入同一个网络

ubuntu@ubuntuserver:~$ sudo docker inspect 0ca051d632b1393a25caf8 | grep NetworkMode
            "NetworkMode": "elasticsearch-kibana",

4. 启动canal-adapter

1. 下载

https://github.com/alibaba/canal.git
我这里下载的是1.1.3

2. idea导入client-adapter项目

找到root模块,install一下,否则直接运行会报错: Load canal adapter: xx failed

注意,如果你的docker部署的mysql是8.x版本,那么需要把launcher模块中的pom文件,mysql依赖的版本号注释掉,这样就会自动依赖mysql connector 8.x版本,低版本的驱动是连不上高版本的mysql的。

3. 修改launcher模块的application.yml

## 这里修改成自己的canal-server地址
canalServerHost: 192.168.0.106:11111

## 这里修改成自己的mysql相关信息
canal.conf:
  mode: tcp # kafka rocketMQ
  canalServerHost: 192.168.0.106:11111
  batchSize: 500
  syncBatchSize: 1000
  retries: 0
  timeout:
  accessKey:
  secretKey:
  srcDataSources:
    defaultDS:
      url: jdbc:mysql://192.168.0.106:3306/chinsblog?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&useSSL=false
      username: canal
      password: canal
  canalAdapters:
  - instance: example # canal instance Name or mq topic name
    groups:
    - groupId: g1
      outerAdapters:
      - name: logger
      - name: es
        hosts: 192.168.0.106:9300
        properties:
          cluster.name: docker-cluster

4. 创建数据库表和elasticsearch索引

我这里是用我项目里的entity,然后通过@test方法实现的。

(我的项目是一个前后端分离的vue + springboot实现的博客项目,以后打算梳理和重构一下,写成文章,方便自己复盘)
## github:
https://github.com/toyranger/chins-blog-backend.git
https://github.com/toyranger/chins-blog-frontend.git
## entity
@Document(indexName = "chinsblog_article", type = "article")
@Data
public class Article {

  @TableId(value = "id", type = IdType.AUTO)
  private Long id;
  private String title;
  private String content;
  private String date;
  private int views;
  private int comments;
  private int thumbs;
}


## test
@Test
public void createArticleIndex() {

  List<Article> articles = articleMapper.selectList(null);
  for (Article article : articles) {
    articleESRepository.index(article);
  }
}

所以现在的kibana上面可以看到我elasticsearch里的索引和数据

5. 创建同步配置文件

在resources目录下的yml文件都可以。

dataSourceKey: defaultDS
destination: example
groupId: g1
esMapping:
## index 和 type,和entity中的注解保持一致,即和elasticsearch中的mapping保持一致
  _index: chinsblog_article
  _type: article
  _id: _id
  upsert: true
  #  pk: id
  ## 注意这里的as _id,否则同步的时候会空指针,而且为什么同步过去的文档,没有id这个字段了???
  sql: "SELECT a.id as _id, a.title, a.content, a.date, a.views, a.comments, a.thumbs FROM chinsblog.article a"
  #  objFields:
  #    _labels: array:;
  #  etlCondition: "where a.c_time>='{0}'"
  commitBatch: 3000

6. 运行launcher的main方法,启动canal-adapter

可以看到,已经连接成功

尝试update和insert表数据,可以看到同步过程以及结果,同时kibana中可以看到同步之后的elasticsearch数据:

kibana:

结束