使用docker+mysql+canal+kafka获取数据库增量数据用于建立数据仓库

软件版本

CentOS Linux release 7.6.1810 (Core) 
Docker version 20.10.3
canal-admin v1.1.5
canal-server v1.1.5
mysql 5.7
mysql 8
wurstmeister/kafka 2.13-2.7.0
zookeeper 3.6.3

docker常用命令

docker官网安装地址

  • 下载镜像(可在dockerHub查找镜像:dockerHub官网地址

     docker pull mysql
    
  • 删除镜像(当没有使用此镜像的容器存在时,才能删除)

     docker rmi mysql
    
  • 运行容器(还有很多参数可以选择,详情查看docker run --help)

     docker run 
     -d  // 后台运行
     --name mysql5.7 //指定名称 
     -p 3305:3306  //端口映射
     -v /home/docker/mysql/5.7/data/mysql:/var/lib/mysql //容器卷映射 
     -e MYSQL_ROOT_PASSWORD=123456 //参数传递
     mysql //指定镜像
    
  • 查询容器(加上-a可以查询所有容器,包括失败容器)

     docker ps
    
  • 重启容器(当容器关闭后才能重启)

     docker restart mysql
    
  • 关闭容器

     docker stop mysql
    
  • 删除容器

     docker rm mysql
    
  • 进入容器

     docker exec -it mysql bash
    

Mysql配置

mysql5.7

1)、docker容器运行(此步骤只是先将容器配置以及数据复制到宿主机)

docker run -d --name mysql5.7 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

2)、拷贝容器配置及数据

\\ 需先建立目录
mkdir /home/docker/mysql/5.7/data
mkdir /home/docker/mysql/5.7/conf
\\ 拷贝数据(这是mysql的表数据以及日志数据)
docker cp mysql5.7:/var/lib/mysql  /home/docker/mysql/5.7/data
\\ 拷贝配置文件
docker cp mysql5.7:/etc/mysql  /home/docker/mysql/5.7/conf

3)、删除容器

docker stop mysql5.7
docker rm mysql5.7

4)、运行加了容器卷映射的容器(注意这里容器卷的映射和上面拷贝的地址是不一样的,多加了一层目录)。这里制定了root账户的密码是123456

docker run  \
-d  \
--name mysql5.7 \
-p 3305:3306  \
-v /home/docker/mysql/5.7/data/mysql:/var/lib/mysql  \
-v /home/docker/mysql/5.7/conf/mysql:/etc/mysql  \
-e MYSQL_ROOT_PASSWORD=123456  \
mysql:5.7

5)、修改配置,开启binlog,以row形式进行增量记录

修改 /home/docker/mysql/5.7/conf/mysql下的my.cnf、mysql.cnf、mysql.conf.d下的或者conf.d下的任一文件。server-id需在mysql中保持唯一

log-bin=/var/lib/mysql/mysql-bin
server-id=123454
binlog-format=ROW

6)、重启容器

docker restart mysql5.7

7)、连接mysql,在mysql中执行下列脚本。查看容器是否开启binlog(为NO则为开启,OFF为没开启)

SHOW  GLOBAL VARIABLES LIKE '%log_bin%';

但是 !!! 我遇到了第一个坑,mysql5.7是默认不开启binlog日志的,我修改宿主机配置文件后重启是没有开启binlog(安装上述方式修改配置文件)。最后找到了另外的方式去修改。
通过shell命令去修改容器中的文件,再重启就成功开启binlog日志。这也是一种修改docker容器中文件的方式。

docker exec mysql5.7 bash -c "echo 'log-bin=/var/lib/mysql/mysql-bin' >> /etc/mysql/mysql.conf.d/mysqld.cnf"
docker exec mysql5.7 bash -c "echo 'server-id=123454' >> /etc/mysql/mysql.conf.d/mysqld.cnf"

mysql8.0

mysql8是默认开启binlog的,可以按照上述方式启动mysql8,不用修改配置。

kafka

先启动zookeeper

docker run -d --name zookeeper -p 2181:2181 zookeeper:3.6.3

启动kafka

docker run -d --name kafka \
-p 9092:9092 \
-e KAFKA_BROKER_ID=0 \
-e KAFKA_ZOOKEEPER_CONNECT=zookeeper的ip:2181 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://当前主机的ip:9092 \
-e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 wurstmeister/kafka:2.13-2.7.0

可以使用Offset Explorer连接kafka查看数据:Offset Explorer下载地址
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

canal-admin

canal-admin是canal-server的界面管理。可要可不要,如果没有canal-admin,canal-server也能使用,稍后会讲解两种方式的canal-server的配置方式。
在这里插入图片描述

1、启动canal-admin

1.1 使用官方shell启动脚本

下载shell脚本(下载失败就多试几次,可能是网络原因)

wget https://raw.githubusercontent.com/alibaba/canal/master/docker/run_admin.sh 

1)、使用canal-admin内置的数据库时候的启动方式(密码需要为6位以上,不然登录不了)

sh run_admin.sh \
    -e server.port=8089 \
    -e canal.adminUser=admin \
    -e canal.adminPasswd=123456 

2)、使用外部的数据库时候的启动方式

sh run_admin.sh \
    -e server.port=8089 \
    -e canal.adminUser=admin \
    -e canal.adminPasswd=123456 \
    -e spring.datasource.address=  \
    -e spring.datasource.database=canal_manager \
    -e spring.datasource.username= \
    -e spring.datasource.password=

外部数据库的脚本:sql地址

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `canal_manager` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */;

USE `canal_manager`;

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for canal_adapter_config
-- ----------------------------
DROP TABLE IF EXISTS `canal_adapter_config`;
CREATE TABLE `canal_adapter_config` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `category` varchar(45) NOT NULL,
  `name` varchar(45) NOT NULL,
  `status` varchar(45) DEFAULT NULL,
  `content` text NOT NULL,
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for canal_cluster
-- ----------------------------
DROP TABLE IF EXISTS `canal_cluster`;
CREATE TABLE `canal_cluster` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(63) NOT NULL,
  `zk_hosts` varchar(255) NOT NULL,
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for canal_config
-- ----------------------------
DROP TABLE IF EXISTS `canal_config`;
CREATE TABLE `canal_config` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `cluster_id` bigint(20) DEFAULT NULL,
  `server_id` bigint(20) DEFAULT NULL,
  `name` varchar(45) NOT NULL,
  `status` varchar(45) DEFAULT NULL,
  `content` text NOT NULL,
  `content_md5` varchar(128) NOT NULL,
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `sid_UNIQUE` (`server_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for canal_instance_config
-- ----------------------------
DROP TABLE IF EXISTS `canal_instance_config`;
CREATE TABLE `canal_instance_config` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `cluster_id` bigint(20) DEFAULT NULL,
  `server_id` bigint(20) DEFAULT NULL,
  `name` varchar(45) NOT NULL,
  `status` varchar(45) DEFAULT NULL,
  `content` text NOT NULL,
  `content_md5` varchar(128) DEFAULT NULL,
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for canal_node_server
-- ----------------------------
DROP TABLE IF EXISTS `canal_node_server`;
CREATE TABLE `canal_node_server` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `cluster_id` bigint(20) DEFAULT NULL,
  `name` varchar(63) NOT NULL,
  `ip` varchar(63) NOT NULL,
  `admin_port` int(11) DEFAULT NULL,
  `tcp_port` int(11) DEFAULT NULL,
  `metric_port` int(11) DEFAULT NULL,
  `status` varchar(45) DEFAULT NULL,
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for canal_user
-- ----------------------------
DROP TABLE IF EXISTS `canal_user`;
CREATE TABLE `canal_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(31) NOT NULL,
  `password` varchar(128) NOT NULL,
  `name` varchar(31) NOT NULL,
  `roles` varchar(31) NOT NULL,
  `introduction` varchar(255) DEFAULT NULL,
  `avatar` varchar(255) DEFAULT NULL,
  `creation_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

-- ----------------------------
-- Records of canal_user
-- ----------------------------
BEGIN;
INSERT INTO `canal_user` VALUES (1, 'admin', '6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9', 'Canal Manager', 'admin', NULL, NULL, '2019-07-14 00:05:28');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

使用shell脚本启动的容器是不显示的端口映射的,因为启动是指定了–net=host,表示直接引用宿主机端口。

1.2 使用docker命令启动

docker run -it --name canal-admin \
	-p 8089:8089 \
    -e server.port=8089 \
    -e canal.adminUser=admin \
    -e canal.adminPasswd=123456 \
    -e spring.datasource.address= \
    -e spring.datasource.database=canal_manager \
    -e spring.datasource.username= \
    -e spring.datasource.password= \  
    -d canal/canal-admin:v1.1.5

启动成功后浏览器访问 ip:8089 查看canal-admin的页面。如果访问失败,则再看canal-admin是否挂掉了。

2 启动失败

如果canal-admin启动失败。
1、内存不足。当内存不足时,最后启动的容器会挂掉
2、数据库连接失败。可以去查看canal-admin的日志。
这里的查看canal-admin的日志不是docker logs去查看启动日志。而是进入容器里面查看日志。就是拼手速,只要手速快,就能在容器挂掉前看到是什么错误。也可以在启动容器时候将日志映射到宿主机。
canal-admin的日志地址。

/home/admin/canal-admin/logs/admin.log

如果出现如下错误:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Public Key Retrieval is not allowed

莫慌,让子弹飞一会。这个错误找了网上好久,但都说是在url上加 allowPublicKeyRetrieval=true。但我在没加这个参数上,多启动了几次mysql,也是连接上了。而且我也在本地项目中使用canal中的数据库连接是没有报上面的错误。所以这个错误我有点困惑。希望知道的小伙伴能再评论区中留下解答
在这里插入图片描述

canal-server

canal-server是真正工作的。是将自己伪装成mysql的从库去获取mysql的binlog并解析,可以自己选择是tcp, kafka, rocketMQ, rabbitMQ这几种中选择一种方式推送解析后的日志数据。我这里选择的是kafka,因为kafka的吞吐量是在mq中是最好的。

1 使用官方shell启动脚本(连接canal-admin)

1.1、启动canal-server

1)、下载shell脚本(下载失败就多试几次,可能是网络原因)。

wget https://raw.githubusercontent.com/alibaba/canal/master/docker/run.sh

密码为canal_manager数据库中canal_user中的passwprd。如果未曾更改那就是下面的密码。

sudo sh run.sh 
    -e canal.admin.manager=canal-admin的ip:8089 \
    -e canal.admin.port=11110 \
    -e canal.admin.user=admin \
    -e canal.admin.passwd=6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9

2)、如果连接成功,则再canal-admin可以看到有个server是启动状态的。
在这里插入图片描述
3)、如果不是启动状态的,就再启动一次,拼手气进入容器去看日志。大概率是密码问题。
日志地址

/home/admin/canal-server/logs/canal/canal.log

1.2、修改配置

1)修改server配置。这个配置的是canal的通用配置,指定数据输出口和一些通用过滤配置。
点击操作-》配置-》载入模板可以看到如下界面
在这里插入图片描述
修改如下配置

# tcp, kafka, rocketMQ, rabbitMQ
canal.serverMode = kafka
kafka.bootstrap.servers = kafka的ip:9092

修改完之后点击保存,再返回,如果返回之后是不是启动状态,那就是配置改的有问题,需要修改。可以再次拼手速去容器里面查看日志。也可以在界面上操作-》日志查看日志。
2)、修改instance配置。这个配置的是数据库以及mq的(主题、分区)。这里是一个instance监听一个mysql。
在这里插入图片描述
3)、点击操作-》修改。也可以是新建instance-》载入模板
在这里插入图片描述
修改如下配置

canal.instance.master.address=数据库的ip:3306
canal.instance.dbUsername=数据库连接名
canal.instance.dbPassword=数据库连接密码

点击保存或修改就可以,返回后如果不是启动状态,则在操作-》日志去看为什么启动失败,大概率是因为数据库的密码问题

Caused by: java.io.IOException: caching_sha2_password Auth failed

出现这个异常,是因为mysql8的认证规则有做修改。需要修改账户的认证规则。

#更新一下用户密码,这里是把账户为canal的密码修改为使用mysql_native_password规则生成,密码为canal。
ALTER USER 'canal'@'%' IDENTIFIED WITH mysql_native_password BY 'canal'; 
#刷新权限
FLUSH PRIVILEGES; 

1 使用docker命令启动(不连接canal-admin)

1.1、启动canal-server

docker run  \
-d  \
-e canal.admin.manager=192.168.174.132:8089  \
-e canal.admin.port=11110  \
-e canal.admin.user=admin  \
-e canal.admin.passwd=4acfe3202a5ff5cf467898fc58aab1d615029441 \
--name canal-server  \
-p 11111:11111 \
 canal/canal-server:v1.1.5

1.2、修改配置

server配置文件,修改上述server配置文件一样

/home/admin/canal-server/conf/canal.properties

instance配置文件,修改上述instance配置文件一样

/home/admin/canal-server/conf/example/instance.properties

获取kakfa数据

1、添加依赖

        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
            <version>2.7.1</version>
        </dependency>

2、添加配置

spring:
  kafka:
    consumer:
      # 是否自动提交offset
      enable-auto-commit: true
      # 默认消费组
      group-id: consumer
      # 当kafka中没有初始offset或offset超出范围时将自动重置offset
      # earliest:重置为分区中最小的offset;
      # latest:重置为分区中最新的offset(消费分区中新产生的数据);
      # none:只要有一个分区不存在已提交的offset,就抛出异常;
      auto-offset-reset: earliest
      bootstrap-servers: 192.168.174.132:9092

3、添加消费代码

@Slf4j
@Component
public class kafkaConsumer {

    @KafkaListener(topics = {"example"})
    public void onMessage3(ConsumerRecord<?, ?> record) {
        log.info("==========================start===================" +
                "\r\ntopic:"+record.topic()+"\r\npartition:"+record.partition()+"\r\noffset:"+record.offset()+"\r\nvalue:"+record.value()+"\r\n" +
        "==========================end===================");
    }

    @KafkaListener(id ="example5.7", topicPartitions = {
            @TopicPartition(topic = "example5.7",  partitionOffsets = @PartitionOffset(partition = "0", initialOffset = "1"))
    })
    public void example5(ConsumerRecord<?, ?> record) {
        log.info("\r\n==========================start5.7===================" +
                "\r\ntopic:"+record.topic()+"\r\npartition:"+record.partition()+"\r\noffset:"+record.offset()+"\r\nvalue:"+record.value()+"\r\n" +
                "==========================end===================");
    }
}

4、查看消费日志

// 新增语句,新增name为‘测试’,version为‘1’的数据
==========================start5.7===================
topic:example5.7
partition:0
offset:16
value:{"data":[{"id":"5","name":"测试","version":"1"}],"database":"user","es":1625556602000,"id":2,"isDdl":false,"mysqlType":{"id":"int","name":"varchar(255)","version":"varchar(255)"},"old":null,"pkNames":["id"],"sql":"","sqlType":{"id":4,"name":12,"version":12},"table":"person","ts":1625556603117,"type":"INSERT"}
==========================end===================

// 修改语句,修改version为‘2’
==========================start5.7===================
topic:example5.7
partition:0
offset:17
value:{"data":[{"id":"5","name":"测试","version":"2"}],"database":"user","es":1625556645000,"id":3,"isDdl":false,"mysqlType":{"id":"int","name":"varchar(255)","version":"varchar(255)"},"old":[{"version":"1"}],"pkNames":["id"],"sql":"","sqlType":{"id":4,"name":12,"version":12},"table":"person","ts":1625556645808,"type":"UPDATE"}
==========================end===================

// 删除语句
==========================start5.7===================
topic:example5.7
partition:0
offset:18
value:{"data":[{"id":"5","name":"测试","version":"2"}],"database":"user","es":1625556925000,"id":4,"isDdl":false,"mysqlType":{"id":"int","name":"varchar(255)","version":"varchar(255)"},"old":null,"pkNames":["id"],"sql":"","sqlType":{"id":4,"name":12,"version":12},"table":"person","ts":1625556925623,"type":"DELETE"}
==========================end===================

// person表添加password字段
==========================start5.7===================
topic:example5.7
partition:0
offset:19
value:{"data":null,"database":"user","es":1625557083000,"id":5,"isDdl":true,"mysqlType":null,"old":null,"pkNames":null,"sql":"ALTER TABLE `user`.`person` \r\nADD COLUMN `password` varchar(255) NULL AFTER `version`","sqlType":null,"table":"person","ts":1625557083503,"type":"ALTER"}
==========================end===================

可以看到isDdl字段,是判断是不是ddl语句,如果是ddl语句,sql字段就有sql语句,不是ddl语句就没有。
DDL(Data Definition Language)数据定义语言:对数据库中的某些对象(例如,database,table)进行管理,如Create,Alter和Drop.
DML(Data Manipulation Language)数据操纵语言:对数据库中的数据进行一些简单操作,如insert,delete,update,select等.
如果DML语句也要有sql语句怎么弄呢?
可以查看官方给出的解释:官方回应dml语句获取sql
本人经过测试是可以的。只是数据需要过滤一些。

==========================start5.7===================
topic:example5.7
partition:0
offset:8
value:{"data":null,"database":"","es":1625215656000,"id":1,"isDdl":false,"mysqlType":null,"old":null,"pkNames":null,"sql":"INSERT INTO `user`.`person`(`name`) VALUES ('神神道道')","sqlType":null,"table":"person","ts":1625215668538,"type":"QUERY"}
==========================end===================

==========================start5.7===================
topic:example5.7
partition:0
offset:9
value:{"data":[{"id":"3","name":"神神道道"}],"database":"user","es":1625215656000,"id":1,"isDdl":false,"mysqlType":{"id":"int","name":"varchar(255)"},"old":null,"pkNames":["id"],"sql":"","sqlType":{"id":4,"name":12},"table":"person","ts":1625215668538,"type":"INSERT"}
==========================end===================

这是一条新增语句,新增一条name为’神神道道’的数据。可以看到这里是打印了两条。一条type为"INSERT",一条为"QUERY"。一条有sql语句,一条没有。所以入库前需要对数据做数据过滤。这个涉及到数据清洗的步骤,之后会出这方面的文章。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值