Canal的简单实用

一、背景

工作中有个需求,当数据库的数据变更时,另外一个系统中的数据要能及时感应到,通过调研知道,监听数据库的binlog可以做到一个准实时的通知,而canal主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费,正好满足需求,此处记录一下canal的简单使用。

二、canal的工作原理

步骤:

  1. canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
  2. mysql master收到dump请求,开始推送binary log给slave(也就是canal)
  3. canal解析binary log对象(原始为byte流)

三、安装canal

1、mysql配置相关

1、检测binlog是否开启

  1. mysql> show variables like 'log_bin';
  2. +---------------+-------+
  3. | Variable_name | Value |
  4. +---------------+-------+
  5. | log_bin | ON |
  6. +---------------+-------+
  7. 1 row in set (0.00 sec)

log_bin的值为ON说明打开了。

2、mysql开启binlog

  1. [mysqld]
  2. #binlog日志的基本文件名,需要注意的是启动mysql的用户需要对这个目录(/usr/local/var/mysql/binlog)有写入的权限
  3. log_bin=/usr/local/var/mysql/binlog/mysql-bin
  4. # 配置binlog日志的格式
  5. binlog_format = ROW
  6. # 配置 MySQL replaction 需要定义,不能和 canal 的 slaveId 重复
  7. server-id=1
  8. # 设置中继日志的路径
  9. relay_log=/usr/local/var/mysql/relaylog/mysql-relay

3、创建canal用户

  1. CREATE USER canal IDENTIFIED BY 'canal';
  2. GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
  3. -- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
  4. FLUSH PRIVILEGES;

2、canal配置相关

1、下载canal

  1. # 1.下载 deployer
  2. $ wget https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.deployer-1.1.5.tar.gz
  3. # 下载适配器,不是必须的
  4. $ wget https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.adapter-1.1.5.tar.gz
  5. # 下载管理台,不是必须的
  6. $ wget https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.admin-1.1.5.tar.gz
  7. # 下载示例程序
  8. $ wget https://github.com/alibaba/canal/releases/download/canal-1.1.5/canal.example-1.1.5.tar.gz
  9. # 2.解压 deployer (解压后的目录要存在)
  10. tar -zxvf canal.deployer-1.1.5.tar.gz -C /Users/huan/soft/canal/deployer/
  11. # 3. 查看 conf 目录结构
  12. $ tree conf
  13. conf
  14. ├── canal.properties
  15. ├── canal_local.properties
  16. ├── example
  17. └── instance.properties
  18. ├── logback.xml
  19. ├── metrics
  20. └── Canal_instances_tmpl.json
  21. └── spring
  22. ├── base-instance.xml
  23. ├── default-instance.xml
  24. ├── file-instance.xml
  25. ├── group-instance.xml
  26. ├── memory-instance.xml
  27. └── tsdb
  28. ├── h2-tsdb.xml
  29. ├── mysql-tsdb.xml
  30. ├── sql
  31. └── create_table.sql
  32. └── sql-map
  33. ├── sqlmap-config.xml
  34. ├── sqlmap_history.xml
  35. └── sqlmap_snapshot.xml

2、配置一个instance

instance:一个instance就是一个消息队列,每个instance通道都有各自的一份配置,因为每个mysql的ip,帐号,密码等信息各不相同。

一个canal server可以存在多个instance

1、复制conf/example文件夹

  1. cp -r conf/example conf/customer

customer可以简单理解为此处我需要链接customer数据库,因此命名为这个。

2、修改instance的配置

vim conf/customer/instance.properties

  1. # mysql集群配置中的serverId概念,需要保证和当前mysql集群中id唯一 (v1.1.x版本之后canal会自动生成,不需要手工指定)
  2. # canal.instance.mysql.slaveId=0
  3. # mysql主库链接地址
  4. canal.instance.master.address=127.0.0.1:3306
  5. # mysql主库链接时起始的binlog文件
  6. canal.instance.master.journal.name=
  7. # mysql主库链接时起始的binlog偏移量
  8. canal.instance.master.position=
  9. # mysql主库链接时起始的binlog的时间戳
  10. canal.instance.master.timestamp=
  11. # mysql数据库帐号(此处的用户名和密码为 安装canal#mysql配置相关#创建canal用户 这一步创建的用户名和密码)
  12. canal.instance.dbUsername=canal
  13. # mysql数据库密码
  14. canal.instance.dbPassword=canal
  15. # mysql 数据解析编码
  16. canal.instance.connectionCharset = UTF-8
  17. # mysql 数据解析关注的表,Perl正则表达式,即我们需要关注那些库和那些表的binlog数据,也可以在canal client api中手动覆盖
  18. canal.instance.filter.regex=.*\\..*
  19. # table black regex
  20. # mysql 数据解析表的黑名单,表达式规则见白名单的规则
  21. canal.instance.filter.black.regex=mysql\\.slave_.*

3、instance注意事项

1、配置需要关注那个库和那个表的binlog

修改 instance.properties文件的这个属性canal.instance.filter.regex,规则如下:

  1. 所有表:.* or .*\\..*
  2. canal schema下所有表: canal\\…*
  3. canal下的以canal打头的表:canal\\.canal.*
  4. canal schema下的一张表:canal\\.test1
  5. 多个规则组合使用:canal\\..*,mysql.test1,mysql.test2 (逗号分隔)
2、mysql链接时的起始位置
  • canal.instance.master.journal.name + canal.instance.master.position : 精确指定一个binlog位点,进行启动
  • canal.instance.master.timestamp : 指定一个时间戳,canal会自动遍历mysql binlog,找到对应时间戳的binlog位点后,进行启动
  • 不指定任何信息:默认从当前数据库的位点,进行启动。(show master status)
3、mysql解析关注表定义
  • 标准的Perl正则,注意转义时需要双斜杠:\
4、mysql链接的编码
  • 目前canal版本仅支持一个数据库只有一种编码,如果一个库存在多个编码,需要通过filter.regex配置,将其拆分为多个canal instance,为每个instance指定不同的编码
5、instance.properties配置参考链接:

https://github.com/alibaba/canal/wiki/AdminGuide

3、canal.properties配置相关

vim conf/canal.properties

  1. # canal server绑定的本地IP信息,如果不配置,默认选择一个本机IP进行启动服务
  2. canal.ip = 127.0.0.1
  3. # canal server提供socket服务的端口
  4. canal.port = 1111
  5. # metrics 端口
  6. canal.metrics.pull.port = 11112
  7. # canal 服务的用户名(客户端连接的时候需要这个用户名和密码,也可以不配置)
  8. canal.user = canal
  9. # canal 服务的密码
  10. canal.passwd = 123456
  11. # tcp, kafka, rocketMQ, rabbitMQ(如果我们要将数据发送到kafka中,则此处写kafka,然后配置kafka的配置,此处以tcp演示)
  12. canal.serverMode = tcp
  13. # 当前server上部署的instance列表,此处写 customer ,则和 conf 目录同级下必须要有一个 customer 文件夹,即上一步我们创建的,如果有多个instance说,则以英文的逗号隔开
  14. canal.destinations = customer
  15. # 如果系统是1个cpu,那么需要将这个并行设置成false
  16. canal.instance.parser.parallel = true

注意事项:

1、canal.destinations配置

在canal.properties定义了canal.destinations后,需要在canal.conf.dir对应的目录下建立同名的文件

比如:

  1. canal.destinations = example1,example2

这时需要创建example1和example2两个目录,每个目录里各自有一份instance.properties.

ps. canal自带了一份instance.properties demo,可直接复制conf/example目录进行配置修改

  1. cp -R example example1/
  2. cp -R example example2/

2、canal.auto.scan配置

如果canal.properties未定义instance列表,但开启了canal.auto.scan时

  • server第一次启动时,会自动扫描conf目录下,将文件名做为instance name,启动对应的instance
  • server运行过程中,会根据canal.auto.scan.interval定义的频率,进行扫描
    1. 发现目录有新增,启动新的instance
    2. 发现目录有删除,关闭老的instance
    3. 发现对应目录的instance.properties有变化,重启instance

3、参考链接

参考链接:https://github.com/alibaba/canal/wiki/AdminGuide

4、启动canal

1、启动canal

  1. # 启动canal server
  2. sh bin/startup.sh

2、查看日志

  1. # canal查看日志
  2. tail -f -n200 logs/canal/canal.log
  3. # 如果canal启动失败则需要查看此日志
  4. tail -f -n200 logs/canal/canal_stdout.log
  5. # 查看instance日志,由上面的配置可知,我们的instance的名字是customer,所以看这个日志.
  6. tail -f -n200 logs/customer/customer.log

3、jdk版本

启动的时候需要注意一下本地JDK的版本,测试时发现使用jdk11不能启动,使用jdk8可以启动。

四、客户端消费canal数据

1、引入依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>com.alibaba.otter</groupId>
  4. <artifactId>canal.client</artifactId>
  5. <version>1.1.5</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>com.alibaba.otter</groupId>
  9. <artifactId>canal.protocol</artifactId>
  10. <version>1.1.5</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>com.alibaba.otter</groupId>
  14. <artifactId>canal.common</artifactId>
  15. <version>1.1.5</version>
  16. </dependency>
  17. </dependencies>

2、编写客户端代码

  1. import com.alibaba.otter.canal.client.CanalConnector;
  2. import com.alibaba.otter.canal.client.CanalConnectors;
  3. import com.alibaba.otter.canal.protocol.CanalEntry;
  4. import com.alibaba.otter.canal.protocol.Message;
  5. import java.net.InetSocketAddress;
  6. import java.util.List;
  7. import java.util.concurrent.TimeUnit;
  8. /**
  9. * canal client api 的使用
  10. * https://github.com/alibaba/canal/wiki/ClientExample
  11. * 测试过程中发现,如果修改一个sql语句,但是修改的值没有发生变化,则此处不会监控到。
  12. * 同一个客户端启动多次,只有一个客户端可以获取到数据
  13. *
  14. * @author huan.fu 2021/5/31 - 上午10:31
  15. */
  16. public class CanalClientApi {
  17. public static void main(String[] args) {
  18. String destination = "customer";
  19. // 创建一个 canal 链接
  20. CanalConnector canalConnector = CanalConnectors.newSingleConnector(new InetSocketAddress("127.0.0.1", 11111), destination, "admin", "admin");
  21. // 链接对应的canal server
  22. canalConnector.connect();
  23. // 订阅那个库的那个表等
  24. /**
  25. * 订阅规则
  26. * 1. 所有表:.* or .*\\..*
  27. * 2. canal schema下所有表: canal\\..*
  28. * 3. canal下的以canal打头的表:canal\\.canal.*
  29. * 4. canal schema下的一张表:canal\\.test1
  30. * 5. 多个规则组合使用:canal\\..*,mysql.test1,mysql.test2 (逗号分隔)
  31. */
  32. canalConnector.subscribe("temp_work\\.customer");
  33. // 回滚到未进行 #ack 的地方,下次fetch的时候,可以从最后一个没有 #ack 的地方开始拿
  34. canalConnector.rollback();
  35. int batchSize = 1000;
  36. while (true) {
  37. // 获取一批数据,不一定会获取到 batchSize 条
  38. Message message = canalConnector.getWithoutAck(batchSize);
  39. // 获取批次id
  40. long batchId = message.getId();
  41. // 获取数据
  42. List<CanalEntry.Entry> entries = message.getEntries();
  43. if (batchId == -1 || entries.isEmpty()) {
  44. System.out.println("没有获取到数据");
  45. try {
  46. TimeUnit.SECONDS.sleep(3);
  47. } catch (InterruptedException e) {
  48. e.printStackTrace();
  49. }
  50. continue;
  51. }
  52. for (CanalEntry.Entry entry : entries) {
  53. if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
  54. continue;
  55. }
  56. CanalEntry.RowChange rowChange;
  57. try {
  58. rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
  59. } catch (Exception e) {
  60. throw new RuntimeException("解析binlog数据出现异常 , data:" + entry.toString(), e);
  61. }
  62. CanalEntry.EventType eventType = rowChange.getEventType();
  63. System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",
  64. entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
  65. entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
  66. eventType));
  67. if (eventType == CanalEntry.EventType.QUERY || rowChange.getIsDdl()) {
  68. System.out.println("sql => " + rowChange.getSql());
  69. }
  70. for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
  71. if (eventType == CanalEntry.EventType.DELETE) {
  72. printColumn(rowData.getBeforeColumnsList());
  73. } else if (eventType == CanalEntry.EventType.INSERT) {
  74. printColumn(rowData.getAfterColumnsList());
  75. } else {
  76. System.out.println("-------> before");
  77. printColumn(rowData.getBeforeColumnsList());
  78. System.out.println("-------> after");
  79. printColumn(rowData.getAfterColumnsList());
  80. }
  81. }
  82. }
  83. canalConnector.ack(batchId);
  84. }
  85. }
  86. private static void printColumn(List<CanalEntry.Column> columns) {
  87. for (CanalEntry.Column column : columns) {
  88. System.out.println(column.getName() + " : " + column.getValue() + " update=" + column.getUpdated());
  89. }
  90. }
  91. }

注意️:

经测试,同一个客户端代码,启动多份,模拟多个客户端同时消费,只有一个客户端可以消费数据。

3、测试结果

  1. 没有获取到数据
  2. 没有获取到数据
  3. ================> binlog[mysql-bin.000014:5771] , name[temp_work,customer] , eventType : UPDATE
  4. -------> before
  5. id : 12 update=false
  6. phone : 123 update=false
  7. address : abcdefg update=false
  8. column_4 : update=false
  9. -------> after
  10. id : 12 update=false
  11. phone : 123 update=false
  12. address : 数据改变 update=true
  13. column_4 : update=false
  14. 没有获取到数据
  15. 没有获取到数据

可以看到获取到了,改变后的数据。

五、消费的binlog不存在

1、单机部署

1、查看数据库中binlog文件的位置

  1. show variables like 'log_bin_basename';

2、找到binlog文件和位置

  1. # 查询 mysql-bin.000026 这个binlog文件,从pos点: 4 开始查起,一共查询查询5条
  2. show binlog events in 'mysql-bin.000026' from 4 limit 5;

3、停止canal-server

4、修改instance下的 instance.properties下的如下配置

  1. # mysql主库链接时起始的binlog文件
  2. canal.instance.master.journal.name=mysql-bin.000026
  3. # mysql主库链接时起始的binlog偏移量
  4. canal.instance.master.position=4
  5. # mysql主库链接时起始的binlog的时间戳
  6. canal.instance.master.timestamp=

5、删除meta.data文件。

注意️:

可能会出现的一个问题: column size is not match for table:temp_work.customer,4 vs 3,这个是因为binlog中的表结构和我们真实库中的表结构对不起来,如果发生了这个问题,参考如下链接:https://github.com/alibaba/canal/wiki/TableMetaTSDB 解决。

2、集群部署

1、停止 canal server、canal client
2、删除 zookeeper 上的 /otter/canal/destination/{instanceId} 目录

六、完整代码

代码路径:https://gitee.com/huan1993/spring-cloud-parent/blob/master/canal/canal-api/src/main/java/CanalClientApi.java

七、参考链接

1、https://github.com/alibaba/canal/wiki/简介

2、https://github.com/alibaba/canal/wiki/AdminGuide

3、https://github.com/alibaba/canal/wiki/常见问题解答

4、https://github.com/alibaba/canal/wiki/TableMetaTSDB

Canal的简单使用的更多相关文章

  1. Canal的简单使用(监控数据库数据的变化)

    原文:https://www.cnblogs.com/java-spring/p/8930740.html canal可以用来监控数据库数据的变化,从而获得新增数据,或者修改的数据,用于实际工作中,比 ...

  2. 阿里Canal安装和代码示例

    Canal的简单使用 canal可以用来监控数据库数据的变化,从而获得新增数据,或者修改的数据,用于实际工作中,比较实用,特此记录一下 Canal简介 canal是应阿里巴巴存在杭州和美国的双机房部署 ...

  3. Docker安装canal、mysql进行简单测试与实现redis和mysql缓存一致性

    一.简介 canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费. 早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求 ...

  4. canal简单安装使用

    canal简介:https://github.com/alibaba/canal 1.数据库配置 首先使用canal需要修改数据库配置 [mysqld] log-bin=mysql-bin # 开启 ...

  5. 缓存一致性和跨服务器查询的数据异构解决方案canal

    当你的项目数据量上去了之后,通常会遇到两种情况,第一种情况应是最大可能的使用cache来对抗上层的高并发,第二种情况同样也是需要使用分库 分表对抗上层的高并发...逼逼逼起来容易,做起来并不那么乐观, ...

  6. Canal 同步异常分析:Could not find first log file name in binary log index file

    文章首发于[博客园-陈树义],点击跳转到原文Canal同步异常分析:Could not find first log file name in binary log index file. 公司搜索相 ...

  7. 【源码】canal和otter的高可靠性分析

    一般来说,我们对于数据库最主要的要求就是:数据不丢.不管是主从复制,还是使用类似otter+canal这样的数据库同步方案,我们最基本的需求是,在数据不丢失的前提下,尽可能的保证系统的高可用,也就是在 ...

  8. MySQL增量订阅&消费组件Canal POC

    POC的目的:1.与MYSQL的对接方式,配置文档2.订阅的延迟3.订阅后宕机消息会不会丢失4.能不能从指定的点开始重新订阅5.高并发写入的时候,日志的顺序是否还能保持,不考虑消费的情况订阅是否会延迟 ...

  9. 【MySQL】通过Binary Log简单实现数据回滚(一)

    一.前言 对,没错,我又水了好一阵子,深刻反思寄几.前段时间,工作项目上出于对excel等批量操作可能出现误操作的问题,要求提供一个能够根据操作批次进行数据回滚的能力.在开发的过程中接触到了MySQL ...

随机推荐

  1. Typora + PicGo做个人知识库

    最近在做个人知识库,考察了一圈各种平台和工具,发现还是直接用文件系统管理Markdown文件更符合我当前的需求.以Markdown文件作为文字载体,以文件目录作为分类结构,承载以计算机知识为主的学习笔 ...

  2. 植入式Web前端开发方法

    上一篇,我讲述了植入式Web前端开发的基本情况,本篇就来探究其开发方法.以下假定CMS只能植入前端代码,并且需求规模是任意大小的. 代码形式 HTML代码是直接植入的毫无疑问,但除非植入的代码非常简短 ...

  3. 10 个不为人知的Python冷知识

    1. 省略号也是对象 ... 这是省略号,在Python中,一切皆对象.它也不例外. 在 Python 中,它叫做 Ellipsis . 在 Python 3 中你可以直接写-来得到这玩意. > ...

  4. 升级到windows10之后的骚操作,安装debian,centos7,支持linux、docker、kubectl命令

    修改Windows10默认字体和图标很大 打开Hyper-V Windows10下载Docker Desktop https://www.docker.com/products/docker-desk ...

  5. PHP中使用if的时候为什么建议将常量放在前面?

    在某些框架或者高手写的代码中,我们会发现有不少人喜欢在进行条件判断的时候将常量写在前面,比如: if(1 == $a){ echo 111; } 这样做有什么好处呢?我们假设一个不小心的粗心大意,少写 ...

  6. Shell系列(6)- 管道符

    多命令顺序执行 多命令执行符 格式 作用 ; 命令1 ; 命令2 连接命令:多个命令顺序执行,命令之间没有任何逻辑联系:前面命令报错,后面命令照常执行 && 命令1 && ...

  7. Typescript, ES6

    * typescript 中文文档 https://www.tslang.cn/docs/home.html * ECMAScript 6 入门 http://es6.ruanyifeng.com/# ...

  8. 执行sudo apt-get update,连接失败

    问题:sudo apt-get update连接失败 错误:1 http://cn.mirrors.ustc.edu.cn/ubuntu bionic InRelease 连接失败 [IP: 218. ...

  9. Centos7安装配置Gitlab-CE

    GitLab介绍 GitLab:是一个基于Git实现的在线代码仓库托管软件,你可以用gitlab自己搭建一个类似于Github一样的系统,一般用于在企业.学校等内部网络搭建git私服. 功能:Gitl ...

  10. 《Android自动化环境搭建》

    一.安装JDK并配置环境变量 1:在Java官网上下载本机系统相对应的jdk文件安装,直接下一步一步到位 2:配置JAVA_HOME 新建 JAVA_HOME 环境变量,变量值是所安装JDK 的路径, ...