前言

千呼万唤始出来,停了好个月,终于又开始动手写文章了,今天带给大家的是阿里的一个工具Canal,这个工具是企业做数据同步使用的比较多的方案,希望对你有所帮助,喜欢的话请给个好评

工作原理分析

我们在面试的时候常常听面试官问这么一个问题:你们的Mysql和Redis怎么做数据同步的,根据不同的业务场景又很多方案,你可能会说先写库再删缓存,或者延迟双删或其他方案。今天我要给大家分享的就是比较成熟的方案-使用Canal实现Mysql和Redis数据的同步。

我不知道你是否了解Mysql主从,根据2/8原则,80%的性能问题都在读上面,当我们数据库的读并发较大的时候,我们可以使用Mysql主从来分担读的压力。它的原理是所有的写操作在主库上,读操作在从库上,当然主库也可以承担读请求,而从库的数据则通过主库复制而来,Mysql自带主从复制的功能。如下图

主从复制步骤:

  1. 将Master的binary-log日志文件打开,mysql会把所有的DDL,DML,TCL写入BinaryLog日志文件中
  2. Master会生成一个 log dump 线程,用来给从库的 i/o线程传binlog
  3. 从库的i/o线程去请求主库的binlog,并将得到的binlog日志写到中继日志(relaylog)中
  4. 从库的sql线程,会读取relaylog文件中的日志,并解析成具体操作,通过主从的操作一致,而达到最终数据一致

而Canal的原理就是伪装成Slave从Binlog中复制SQL语句或者数据。

Mysql和Redis数据同步方案

根据上面所说,我们就可以通过Canal去自动同步数据库的binlog数据日志文件,然后再把数据同步到Redis,从而达到Mysql和Redis自动同步的功能。很遗憾的是Canal没办法直接把数据库同步到Redis,它支持的是组件有 : mysql、Kafka、ElasticSearch、Hbase、RocketMQ等

当然 canal 特别设计了 client-server 模式,交互协议使用 protobuf 3.0 , client 端可采用不同语言实现不同的消费逻辑

  • canal java 客户端: https://github.com/alibaba/canal/wiki/ClientExample
  • canal c# 客户端: https://github.com/dotnetcore/CanalSharp
  • canal go客户端: https://github.com/CanalClient/canal-go
  • canal Python客户端: https://github.com/haozi3156666/canal-python

canal 作为 MySQL binlog 增量获取和解析工具,可将变更记录投递到 MQ 系统中,比如 Kafka/RocketMQ,可以借助于 MQ 的多语言能力,因此我们可以使用下面这种方案来同步数据

这里对流程做一个解释

  1. 首选我们需要开启Mysql的bin-log日志文件
  2. 接着安装好Canal去同步bin-log日志,同时配置RocketMQ地址,Canal会把消息推送给MQ
  3. 需要编写Java客户端去监听MQ中的消息,然后往Redis中进行同步

开启Mysql bin-log日志

找到Mysql安装目录中的my.ini 配置文件,我以mysql 5.5为例,在 mysqld 下做如下配置

[mysqld]
#开启bInlog
log-bin=mysql-bin
#给mysql服务指定一个唯一的ID
server-id=1
#以数据的方式写binlog日志 :statement 是记录SQL,row是记录数据
binlog-format=ROW
#同步的数据库名
binlog-do-db=canaldb
#忽略的表
binlog-ignore-db=mysql
# 启动mysql时不启动grant-tables授权表
skip-grant-tables

修改好之后,重启Mysql服务。注意:我这里指定了需要同步的数据库为canaldb,所以需要创建一个数据库,同时创建了一个employee表作为演示

然后创建一个用户提供给canal来链接Mysql做数据同步

flush privileges;
#创建用户cannal
CREATE USER canal IDENTIFIED BY 'canal';
#把所有权限赋予canal,密码也是canal
GRANT ALL PRIVILEGES ON canaldb.user TO 'canal'@'%' identified by "canal";
//GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%' identified by "canal";
#刷新权限
flush privileges;

到这,Mysql部分就搞定了

安装Canal

去官网下载 Canal : https://github.com/alibaba/canal/releases ,我使用的是canal.deployer-1.1.5.tar.gz版本

下载好之后解压,目录结构如下

接下来修改instance 配置文件 : conf/example/instance.properties

#  按需修改成自己的数据库信息
#################################################
...
#我的端口是3307
canal.instance.master.address=192.168.1.20:3307
# username/password,数据库的用户名和密码
...
#刚才开通的mysql的账户密码
canal.instance.dbUsername = canal
canal.instance.dbPassword = canal
...
################################################## mq config 数据同步到MQ中的topic名字
canal.mq.topic=example
# 针对库名或者表名发送动态topic
#canal.mq.dynamicTopic=mytest,.*,mytest.user,mytest\\..*,.*\\..*
canal.mq.partition=0
# hash partition config
#canal.mq.partitionsNum=3
#库名.表名: 唯一主键,多个表之间用逗号分隔
#canal.mq.partitionHash=mytest.person:id,mytest.role:id

这里注意如下几个东西,其他的不用管

  • master.address :Mysql的地址,我的端口是3307,默认是3306
  • dbUsername :上面开通的Mysql用户
  • dbPassword : 密码
  • canal.mq.topic=example : 数据同步到MQ中的topic名字

接着修改canal 配置文件 conf/canal.properties

# ...
# 可选项: tcp(默认), kafka, RocketMQ
# 这里使用RocketMQ
canal.serverMode = rocketMQ
# ...
##################################################
#########           RocketMQ         #############
##################################################
rocketmq.producer.group = test
rocketmq.enable.message.trace = false
rocketmq.customized.trace.topic =
rocketmq.namespace =
# RocketMQ的地址
rocketmq.namesrv.addr = 127.0.0.1:9876
rocketmq.retry.times.when.send.failed = 0
rocketmq.vip.channel.enabled = false
rocketmq.tag =

这里需要注意2个东西

  • canal.serverMode = rocketMQ : 我这里以RocketMQ为例
  • rocketmq.namesrv.addr = 127.0.0.1:9876 : 指向RocketMQ的地址

配置好之后,找到 canal 安装目录下 bin目录下的 startup.bat 双击启动,linux上启动:startup.sh

对于RocketMQ没使用过的童鞋可以看我《RocketMQ极简入门专题》,启动Canal和RocketMQ之后,尝试往employee表中增加数据,观察MQ控制台是否有数据同步,访问http://localhost:8080/#/message 如下

点击 MESSAGE DETAIL 如下

数据是JSON格式存储到MQ中的 , 这里可以看到数据同步于哪个数据库,哪个表,以及数据内容以及old老数据。注意这里的内容格式,后面我们需要封装对象

Java程序把数据同步到Redis

接下来需要编写一个Java程序来消费MQ中的消息同步到Redis ,创建项目,导入依赖

<parent><groupId> org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version>
</parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
<dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.0.4</version>
</dependency>
<!--整合Redis , 底层可以用jedis-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions>
</dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId>
</dependency></dependencies>

编写yml配置MQ和Redis

#..其他省略..
spring:redis:database: 0host: 127.0.0.1port: 6379password: 123456jedis:pool:max-wait: 2000msmin-idle: 2max-idle: 8
rocketmq:name-server: 127.0.0.1:9876# 是否开启自动配置producer:enable-msg-trace: truegroup: "service-producer"# 消息最大长度 默认 1024 * 4 (4M)max-message-size: 4096# 发送消息超时时间,默认 3000send-message-timeout: 3000# 发送消息失败重试次数,默认2retry-times-when-send-failed: 2retry-times-when-send-async-failed: 2

编写实体类来封装MQ中的消息 , 注意: 下面的 CanalSynDto对象是根据MQ中的消息内容进行封装的,你可以把数据复制到在线JSON转换工具中进行分析。

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {private Long id;private String username;
}@AllArgsConstructor
@NoArgsConstructor
@Data
public class CanalSynDto {private List<Employee> data;private String database;private String table;private String type;//省略了一些不重要的内容
}

编写MQ消费者代码,把MQ消息封装成 CanalSynDto 对象,然后取到data数据,再根据SQL的类型(insert,delete,update)对Redis进行数据同步

@Slf4j
@Component
//对应了canal的instance.properties 中的canal.mq.topic=example
@RocketMQMessageListener(topic = "example", //TOPIC主题,selectorExpression="*" //tag标签,consumerGroup = "canal-syn-consumer",messageModel = MessageModel.CLUSTERING
)
public class CanalSynListenner  implements RocketMQListener<MessageExt> {//注入Redis API@Autowiredprivate RedisTemplate<Object,Object> redisTemplate;@Overridepublic void onMessage(MessageExt message) {try {//拿到MQ中的消息内容String json = new String(message.getBody(), "utf-8");//把数据转为实体类CanalSynDto canalSynDto = JSON.parseObject(json, CanalSynDto.class);log.info("canal同步 {}", canalSynDto);//如果是INSERT或者UPDATE,直接往Redis添加if(canalSynDto.getType().equals("INSERT") || canalSynDto.getType().equals("UPDATE")){//insert就添加,update就覆盖canalSynDto.getData().forEach(employee -> {//以  ID为key,把对象存储到Redis中redisTemplate.opsForValue().set("ID:"+employee.getId(),employee);});//删除命令}else if (canalSynDto.getType().equals("DELETE")){canalSynDto.getData().forEach(employee -> {//以  ID为key,把对象从Redis中删除redisTemplate.delete("ID:"+employee.getId());});}} catch (UnsupportedEncodingException e) {e.printStackTrace();}}
}

到这里代码就写完了,启动SpringBoot程序,对employee表中数据进行修改或者删除,观察Redis中的数据变化 :Redis始终会随着Mysql变化而变化

我们的效果达到了,弱弱的问一句:Mysql实现ElasticSearch的数据同步你有方案吗?可以在评论区说出你的见解

文章结束,喜欢的话请一定给个好评哦,你的鼓励是我最大的动力.

使用canal解决Mysql和Redis数据同步问题相关推荐

  1. Canal 实现 Mysql数据库实时数据同步

    简介 1.1 canal介绍 Canal是一个基于MySQL二进制日志的高性能数据同步系统.Canal广泛用于阿里巴巴集团(包括https://www.taobao.com),以提供可靠的低延迟增量数 ...

  2. 基于Canal的MySQL=>ES数据同步方案

    文章目录 1.MySQL和ES的主要区别? 1.1 功能性 1.2 性能指标 1.3 在搜索业务上的区别 1.3.1 查询 1.3.2 检索 2.为什么要做数据同步 2.1 检索性能 2.2 写入性能 ...

  3. canal+Kafka实现mysql与redis数据同步

    前言 上篇文章简单介绍canal概念,本文结合常见的缓存业务去讲解canal使用.在实际开发过程中,通常都会把数据往redis缓存中保存一份,做下简单的查询优化.如果这时候数据库数据发生变更操作,就不 ...

  4. Canal监控MySQL数据库实现数据同步

    canal是阿里巴巴旗下的一款开源项目,纯Java开发.基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL. canal简介 需求:将MySQL中某些表的数据实时的同步到 ...

  5. mysql与redis数据同步(c/c++)(写mysql同步到redis,并且以json格式保存)

    系统开发中时常会需要缓存来提升并发读的能力,这时可以通过mysql的UDF和hiredis来进行同步 原理: 通过mysql自动同步redis 在服务端开发过程中,一般会使用MySQL等关系型数据库作 ...

  6. Mysql和Redis数据同步策略

    为什么对缓存只删除不更新 不更新缓存是防止并发更新导致的数据不一致. 所以为了降低数据不一致的概率,不应该更新缓存,而是直接将其删除, 然后等待下次发生cache miss时再把数据库中的数据同步到缓 ...

  7. Mysql和Redis数据同步该怎么做

    前言 算法血拼:Google+百度+Alibaba+字节+Tencent+网易+360+拼夕夕+美团 不知不觉双11就来了,轰轰烈烈的秋招也完美结束了,不知算法与数据结构成为了多少小伙伴进击大厂的绊脚 ...

  8. 灵魂拷问!Mysql和Redis数据同步该怎么做?请查收

    前言 我们从一个问题引入今天的主题. 在日常业务开发中,我们可能经常听到 DBA 对我们说"不要"(注意:不是禁止)使用 join,那么为什么 DBA 对 join 这么抵触呢?是 ...

  9. Mysql和Redis数据同步该怎么做?含答案解析

    前言 关于技术人如何成长的问题,一直以来都备受关注,因为程序员职业发展很快,即使是相同起点的人,经过几年的工作或学习,会迅速拉开极大的差距,所以技术人保持学习,提升自己,才能够扛得住不断上赶的后浪,也 ...

最新文章

  1. 数组专题——找重复数字 利用下标
  2. 多多客DOODOOKE更新插件模块及下载附件教程
  3. avcodec_encode_video2 -22
  4. FishC01 讲:我和 Python 第一次亲密接触
  5. 百度地图API —— Hello World!
  6. 奥委会主席巴赫与马云对谈:阿里巴巴能将奥运精神带进科技时代
  7. Page_PreInit在网页传值的应用
  8. SDOI2016R2(怎么可能是解题报告)
  9. (三.0)通过FPGA实现以太网通信原理及理解
  10. Unix环境高级编程第三版源代码编译与使用说明
  11. 关于机械振动以及故障诊断
  12. android9 三星 港版,三星S9+官方港版安卓9完整固件系统升级包:TGY-G9650ZHU5CSFB
  13. 二十一世纪大学英语读写基础教程学习笔记(原文)——4 - The Happiest Man in the World(世界上最幸福的人)
  14. html显示vbs变量,VBS 读取 对象某属性已连接的变量的变量名
  15. 浅谈Go 语言之 go-app
  16. javascript小技巧
  17. winform 中自定义有透明度的panel,可用于加载遮盖层
  18. [转] 我应该早看的 狂后悔啊 200条装修小常识 [图片]
  19. oracle中overwrite写法,【学习笔记】Oracle 11G新特性restart的深入研究案例
  20. 谷歌IO大会全面硬钢微软+OpenAI

热门文章

  1. python依赖打包
  2. HTTP协议图片上传交互
  3. Kali Linux安装教程(亲测)
  4. SQL SERVER 通过SQL得到一百年内每周的开始日期和结束日期
  5. MMDetection3D简单学习
  6. 三孔插头三根线的区分
  7. 台湾作家林清玄去世 多篇作品入选大陆语文教材
  8. Python匹配两个列表
  9. 于变革时代探寻破局之光 | LeaTech全球CTO领导力峰会圆满落幕
  10. CDC(变化数据捕获)同步技术详解