前面提到数据库缓存不一致的几种解决方案,但是在不同的场景下各有利弊,而今天我们使用的canal进行缓存与数据同步的方案是最好的,但是也有一个缺点,就是相对前面几种解决方案会引入阿里巴巴的canal组件,订阅消费binlog日志,增加的系统复杂度。

canal官网地址:https://github.com/alibaba/canal/wiki

简介:

canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费

工作原理

  • canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送 dump 协议

  • MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )

  • canal 解析 binary log 对象(原始为 byte 流)

第一步:创建MySQL配置文件

创建MySQL文件目录
mkdir -p /home/mysql/{master,slave}/{logs,data,conf,mysql-files}
在/home/mysql/master/conf目录下创建my.cnf配置文件并写入一下信息


#主从复制-主机配置
[mysqld]
#设置主机启动端口3316端口
port=3316
#主服务器唯一ID
server-id = 1
#启用二进制日志
log-bin=mysql-bin
#设置logbin格式
binlog_format = Row
#设置mysql的安装目录
#basedir=
#设置mysql数据库的数据的存放目录
datadir=/home/mysql/master/data
#允许最大连接数
max_connections=200
#允许连接失败的次数
max_connect_errors=10
#服务端使用的字符集默认为utf8mb4
character-set-server=utf8mb4
#创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
#默认使用mysql_native_password插件认证
#mysql_native_password
default_authentication_plugin=mysql_native_password
#忽略大小写
lower-case-table-names = 1
[mysql]
#设置mysql客户端默认字符集
default-character-set=utf8mb4
#MySQL导入导出文件限制
#secure_file_priv =

[client]
#设置mysql客户端连接服务端时默认使用的端口
port=3316
default-character-set=utf8mb4

第二步:拉取MySQL镜像

docker pull mysql

第三步:创建MySQL容器

docker run \
-p 3316:3316 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v /home/mysql/master/data:/home/mysql/master/data:rw \
-v /home/mysql/master/logs:/var/log/mysql:rw \
-v /home/mysql/master/conf/my.cnf:/etc/mysql/my.cnf:rw \
-v /home/mysql/mster/mysql-files:/var/lib/mysql-files \
-v /etc/localtime:/etc/localtime:ro \
--name mysql3316_master \
--privileged=true \
--restart=always \
-d mysql

第四步:创建canal进行数据同步的MySQL用户

create user canal@'%' IDENTIFIED by 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%' ;
FLUSH PRIVILEGES;

第五步:创建canal

docker run -p 11111:11111 --name canal \
-e canal.destinations=canal_test \
-e canal.instance.master.address=192.168.0.100:3316 \
-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=canal_test\\..* \
--privileged=true \
-d canal/canal-server:v1.1.5

说明:

  • -p 11111:11111:这是canal的默认监听端口

  • -e canal.instance.master.address=mysql:3316

  • -e canal.instance.dbUsername=canal:数据库用户名

  • -e canal.instance.dbPassword=canal :数据库密码

  • -e canal.instance.filter.regex=:要监听的表名称

表名称监听支持的语法:

mysql 数据解析关注的表,Perl正则表达式.
多个正则之间以逗号(,)分隔,转义符需要双斜杠(\\)
常见例子:
1. 所有表:.*   or .*\\..*
2. canal schema下所有表: canal\\..*
3. canal下的以canal打头的表:canal\\.canal.*
4. canal schema下的一张表:canal.test1
5. 多个规则组合使用然后以逗号隔开:canal\\..*,mysql.test1,mysql.test2

查看canal是否创建成功

docker exec -it canal bash 进入容器内部

查看日志,若显示canal正在进行数据同步,则说明搭建成功

第六步:测试

创建springboot项目

pom.xml添加依赖

<dependency>
  <groupId>top.javatool</groupId>
  <artifactId>canal-spring-boot-starter</artifactId>
  <version>1.2.1-RELEASE</version>
</dependency>

application.yml文件添加配置

canal:
destination: canal_test
server: master:11111

创建测试实体类

import org.springframework.data.annotation.Id;

import javax.persistence.Column;
import javax.persistence.Table;

@Table(name = "order")
public class Order {
  @Id
  @Column(name = "order_id")
  private Long orderId;

  @Column(name = "order_name")
  private String orderName;

  @Column(name = "order_status")
  private Integer orderStatus;

  @Column(name = "user_id")
  private Long userId;

  public Long getOrderId() {
      return orderId;
  }

  public void setOrderId(Long orderId) {
      this.orderId = orderId;
  }

  public String getOrderName() {
      return orderName;
  }

  public void setOrderName(String orderName) {
      this.orderName = orderName;
  }

  public Integer getOrderStatus() {
      return orderStatus;
  }

  public void setOrderStatus(Integer orderStatus) {
      this.orderStatus = orderStatus;
  }

  public Long getUserId() {
      return userId;
  }

  public void setUserId(Long userId) {
      this.userId = userId;
  }

  @Override
  public String toString() {
      return "Order{" +
              "orderId=" + orderId +
              ", orderName='" + orderName + '\'' +
              ", orderStatus=" + orderStatus +
              ", userId=" + userId +
              '}';
  }
}

创建测试handler

import org.springframework.stereotype.Component;
import top.javatool.canal.client.annotation.CanalTable;
import top.javatool.canal.client.context.CanalContext;
import top.javatool.canal.client.handler.EntryHandler;
import top.javatool.canal.client.model.CanalModel;

@Component
@CanalTable("order")
public class TestHandler implements EntryHandler<Order> {

  @Override
  public void insert(Order order) {
      System.out.println("insert:" + order);
      CanalModel canal = CanalContext.getModel();

      System.out.println("================================================");
      System.out.println(canal);
      System.out.println("================================================");

  }

  @Override
  public void update(Order before, Order after) {
      System.out.println("update before:" + before);
      System.out.println("update after:" + after);
      CanalModel canal = CanalContext.getModel();
      System.out.println("================================================");
      System.out.println(canal);
      System.out.println("================================================");

  }

  @Override
  public void delete(Order order) {
      System.out.println("delete:" + order);
      CanalModel canal = CanalContext.getModel();
      System.out.println("================================================");
      System.out.println(canal);
      System.out.println("================================================");

  }
}

启动springboot项目

随意一下修改数据则可以看到以下日志

  version: 1
logfileName: "mysql-bin.000004"
logfileOffset: 2217
serverId: 1
serverenCode: "UTF-8"
executeTime: 1630830321000
sourceType: MYSQL
schemaName: ""
tableName: ""
eventLength: 90
}
entryType: TRANSACTIONBEGIN
storeValue: " +"
, header {
version: 1
logfileName: "mysql-bin.000004"
logfileOffset: 2378
serverId: 1
serverenCode: "UTF-8"
executeTime: 1630830321000
sourceType: MYSQL
schemaName: "canal_test"
tableName: "order"
eventLength: 67
eventType: UPDATE
props {
  key: "rowsCount"
  value: "1"
}
}
entryType: ROWDATA
storeValue: "\bf\020\002P\000b\263\002\n\034\b\000\020\004\032\border_id \001(\0000\000B\0011R\003int\n\'\b\001\020\f\032\norder_name \000(\0000\000B\001aR\fvarchar(255)\n*\b\002\020\f\032\forder_status \000(\0000\000B\00212R\fvarchar(255)\n$\b\003\020\f\032\auser_id \000(\0000\000B\0011R\fvarchar(255)\022\034\b\000\020\004\032\border_id \001(\0000\000B\0011R\003int\022(\b\001\020\f\032\norder_name \000(\0010\000B\00212R\fvarchar(255)\022*\b\002\020\f\032\forder_status \000(\0000\000B\00212R\fvarchar(255)\022$\b\003\020\f\032\auser_id \000(\0000\000B\0011R\fvarchar(255)"
, header {
version: 1
logfileName: "mysql-bin.000004"
logfileOffset: 2445
serverId: 1
serverenCode: "UTF-8"
executeTime: 1630830321000
sourceType: MYSQL
schemaName: ""
tableName: ""
eventLength: 31
}
entryType: TRANSACTIONEND
storeValue: "\022\003300"
],raw=false,rawEntries=[]]
update before:Order{orderId=null, orderName='a', orderStatus=null, userId=null}
update after:Order{orderId=1, orderName='12', orderStatus=12, userId=1}
================================================
CanalModel{id=4, database='canal_test', table='order', executeTime=1630830321000, createTime=null}
================================================

至此说明我们的canal搭建和使用都没有问题,这种数据同步方式可以极大的减少缓存与数据库的数据不一致问题。

canal数据同步的更多相关文章

  1. canal数据同步的环境配置

    canal数据同步的环境配置:(适用于mysql) 前提:在linux和windows系统的mysql数据库中创建相同结构的数据库和表,我的linux中mysql是用docker实现的(5.7版本), ...

  2. canal数据同步目录

    我们公司对于数据同步有以下需求 1.多个mysql库中有一些基础表需要数据统一,mysql跨库同步 2.mysql热数据加载到redis 3.全文检索需要mysql同步到es 4.数据变更是附属的其它 ...

  3. Canal - 数据同步 - 阿里巴巴 MySQL binlog 增量订阅&消费组件

    背景 早期,阿里巴巴 B2B 公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求 ,主要是基于trigger的方式获取增量变更.从 2010 年开始,公司开始逐步尝试数据库日志解析,获取增量变 ...

  4. canal数据同步 客户端代码实现

    1.引入相关依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId ...

  5. 开源数据同步神器——canal

    前言 如今大型的IT系统中,都会使用分布式的方式,同时会有非常多的中间件,如redis.消息队列.大数据存储等,但是实际核心的数据存储依然是存储在数据库,作为使用最广泛的数据库,如何将mysql的数据 ...

  6. 数据同步canal客户端

    1.增量订阅.消费设计 get/ack/rollback协议介绍: ① Message getWithoutAck(int batchSize),允许指定batchSize,一次可以获取多条,每次返回 ...

  7. 阿里Canal框架(数据同步中间件)初步实践

    最近在工作中需要处理一些大数据量同步的场景,正好运用到了canal这款数据库中间件,因此特意花了点时间来进行该中间件的的学习和总结. 背景介绍 早期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存 ...

  8. Canal——增量同步MySQL数据到ElasticSearch

    1.准备 1.1.组件 JDK:1.8版本及以上: ElasticSearch:6.x版本,目前貌似不支持7.x版本:       Kibana:6.x版本:     Canal.deployer:1 ...

  9. 【Canal】互联网背景下有哪些数据同步需求和解决方案?看完我知道了!!

    写在前面 在当今互联网行业,尤其是现在分布式.微服务开发环境下,为了提高搜索效率,以及搜索的精准度,会大量使用Redis.Memcached等NoSQL数据库,也会使用大量的Solr.Elastics ...

随机推荐

  1. @ControllerAdvice全局异常处理不起作用原因及解决办法

    这段时间使用springboot搭建基础框架,作为springboot新手,各种问题都有. 当把前端框架搭建进来时,针对所有controller层的请求,所发生的异常,需要有一个统一的异常处理,然后返 ...

  2. mysql 占用90%多的CPU,解决思路

    网站打开很慢,爆出了连接数据库的错误,进入服务器,top 看了下,mysql占用cpu 基本维持在90以上: mysql> show variables like '%slow%';      ...

  3. MySQL Shell import_table数据导入

    目录 1. import_table介绍 2. Load Data 与 import table功能示例 2.1 用Load Data方式导入数据 2.2 用import_table方式导入数据 3. ...

  4. POJ 1190 生日蛋糕题解

    题目地址:http://poj.org/problem?id=1190 一道很有趣的搜索题--主要是剪枝-- 我弄了5个剪枝: 1.当前剩余层数>=上层半径,剪掉 2.当前剩余层数>=上层 ...

  5. IPSec组播概要

    IPSec作为主流IP安全协议之一,在单播环境下,特别是在VPN场景中应用广泛.但是在组播环境貌似看到的不多,通过RFC4301了解到IPSec首先是支持组播的,即通过手动配置的方式可以实现组播包加密 ...

  6. Java练习——加减乘除计算器实现

    Java练习--计算器(加减乘除)  package method; import java.util.Scanner; /*  写一个计算器 实现加减乘除四个功能   并且能够用循环接收新的数据,通 ...

  7. 且看一文梳理VS2019中dll的创建使用

    动态链接库(dll) Windows下有静态链接(lib)库和动态链接库(dll)两种共享代码的方式. 本文将介绍dll的应用场景,以及在vs2019平台下的生成和使用. 今天的笔记内容说的是平时经常 ...

  8. Groovy正则表达式复杂逻辑判断实例

    下面的两个pattern(p1和p2)分别代表了(A or B) and (C or D)和(A and B) or (C and D)的跨行匹配结果,当然还可以用正则表达式构建更复杂的pattern ...

  9. IDEA永久使用!!(很全)

    IDEA虽然好用,但是下载后只能试用30天,烦恼呀!所以今天就带来IDEA的激活版来帮助大家摆脱30天的苦恼! 准备工作: 破解所需要的软件和jar都在网盘里,需要的自行下载,在这里idea安装就不带 ...

  10. Django 反向解析 request CBV

    正则路径中的分组 无名分组 分组的概念:就是给某一段正则表达式用小括号括起来 无名分组按位置传参数,一一对应. view中除去request,其他形参数量要与urls中分组数量一致. 无名分组就是将括 ...