Canal解决Redis缓存与Mysql数据库的一致性问题

2024-03-29 16:04

本文主要是介绍Canal解决Redis缓存与Mysql数据库的一致性问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1、什么是Canal?

如何解决Redis缓存与Mysql数据库的一致性问题?我们常用数据双删+缓存超时设置去解决。这样最差的情况,就是在超时时间内,数据存在不一致。

canal,译为管道,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费

canal的工作原理就是把自己伪装成MySQL slave,模拟MySQL slave的交互协议向MySQL Mater发送 dump协议,MySQL mater收到canal发送过来的dump请求,开始推送binary log给canal,然后canal解析binary log,再发送到存储目的地,比如MySQL,Kafka,Elastic Search等等。

2、Canal能做什么?

阿里官方文档+源码 项目概览 - canal - GitCode

canal的数据同步不是全量的,而是增量。基于binary log增量订阅和消费,canal可以做:

  • 数据库镜像
  • 数据库实时备份
  • 索引构建和实时维护
  • 业务cache(缓存)刷新
  • 带业务逻辑的增量数据处理

3、如何搭建Canal?

3.1 Mysql配置

在MySQL中需要创建一个用户,并授权:

-- 使用命令登录:mysql -u root -p
-- 创建用户 用户名:canal 密码:Canal@123456
create user 'canal'@'%' identified by 'Canal@123456';
-- 授权 *.*表示所有库
grant SELECT, REPLICATION SLAVE, REPLICATION CLIENT on *.* to 'canal'@'%' identified by 'Canal@123456';

下一步在MySQL配置文件my.cnf设置如下信息:

[mysqld]
# 打开binlog
log-bin=mysql-bin
# 选择ROW(行)模式
binlog-format=ROW
# 配置MySQL replaction需要定义,不要和canal的slaveId重复
server_id=1

改了配置文件之后,重启MySQL,使用命令查看是否打开binlog模式:

查看binlog日志文件列表:

查看当前正在写入的binlog文件:

3.2 安装canal

去官网下载页面进行下载:https://github.com/alibaba/canal/releases 

解压canal.deployer-1.1.4.tar.gz,我们可以看到里面有四个文件夹:

接着打开配置文件conf/example/instance.properties,配置信息如下:

## mysql serverId , v1.0.26+ will autoGen
## v1.0.26版本后会自动生成slaveId,所以可以不用配置
# canal.instance.mysql.slaveId=0# 数据库地址
canal.instance.master.address=127.0.0.1:3306
# binlog日志名称
canal.instance.master.journal.name=mysql-bin.000001
# mysql主库链接时起始的binlog偏移量
canal.instance.master.position=154
# mysql主库链接时起始的binlog的时间戳
canal.instance.master.timestamp=
canal.instance.master.gtid=# username/password
# 在MySQL服务器授权的账号密码
canal.instance.dbUsername=canal
canal.instance.dbPassword=Canal@123456
# 字符集
canal.instance.connectionCharset = UTF-8
# enable druid Decrypt database password
canal.instance.enableDruid=false# table regex .*\\..*表示监听所有表 也可以写具体的表名,用,隔开
canal.instance.filter.regex=.*\\..*
# mysql 数据解析表的黑名单,多个表用,隔开
canal.instance.filter.black.regex=

修改一下启动的脚本startup.bat:

 我这里用的是win10系统,所以在bin目录下找到startup.bat启动:

启动成功 

4、Java客户端操作

首先引入maven依赖:

<dependency><groupId>com.alibaba.otter</groupId><artifactId>canal.client</artifactId><version>1.1.4</version>
</dependency>

然后创建一个canal项目,使用SpringBoot构建,如图所示:

在CannalClient类使用Spring Bean的生命周期函数afterPropertiesSet():

@Component
public class CannalClient implements InitializingBean {private final static int BATCH_SIZE = 1000;@Overridepublic void afterPropertiesSet() throws Exception {// 创建链接CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("127.0.0.1", 11111), "example", "", "");try {//打开连接connector.connect();//订阅数据库表,全部表connector.subscribe(".*\\..*");//回滚到未进行ack的地方,下次fetch的时候,可以从最后一个没有ack的地方开始拿connector.rollback();while (true) {// 获取指定数量的数据Message message = connector.getWithoutAck(BATCH_SIZE);//获取批量IDlong batchId = message.getId();//获取批量的数量int size = message.getEntries().size();//如果没有数据if (batchId == -1 || size == 0) {try {//线程休眠2秒Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}} else {//如果有数据,处理数据printEntry(message.getEntries());}//进行 batch id 的确认。确认之后,小于等于此 batchId 的 Message 都会被确认。connector.ack(batchId);}} catch (Exception e) {e.printStackTrace();} finally {connector.disconnect();}}/*** 打印canal server解析binlog获得的实体类信息*/private static void printEntry(List<Entry> entrys) {for (Entry entry : entrys) {if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {//开启/关闭事务的实体类型,跳过continue;}//RowChange对象,包含了一行数据变化的所有特征//比如isDdl 是否是ddl变更操作 sql 具体的ddl sql beforeColumns afterColumns 变更前后的数据字段等等RowChange rowChage;try {rowChage = RowChange.parseFrom(entry.getStoreValue());} catch (Exception e) {throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(), e);}//获取操作类型:insert/update/delete类型EventType eventType = rowChage.getEventType();//打印Header信息System.out.println(String.format("================》; binlog[%s:%s] , name[%s,%s] , eventType : %s",entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),eventType));//判断是否是DDL语句if (rowChage.getIsDdl()) {System.out.println("================》;isDdl: true,sql:" + rowChage.getSql());}//获取RowChange对象里的每一行数据,打印出来for (RowData rowData : rowChage.getRowDatasList()) {//如果是删除语句if (eventType == EventType.DELETE) {printColumn(rowData.getBeforeColumnsList());//如果是新增语句} else if (eventType == EventType.INSERT) {printColumn(rowData.getAfterColumnsList());//如果是更新的语句} else {//变更前的数据System.out.println("------->; before");printColumn(rowData.getBeforeColumnsList());//变更后的数据System.out.println("------->; after");printColumn(rowData.getAfterColumnsList());}}}}private static void printColumn(List<Column> columns) {for (Column column : columns) {System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());}}
}

以上就完成了Java客户端的代码。这里不做具体的处理,仅仅是打印,先有个直观的感受。

最后我们开始测试,首先启动MySQL、Canal Server,还有刚刚写的Spring Boot项目。然后创建表:

CREATE TABLE `tb_commodity_info` (`id` varchar(32) NOT NULL,`commodity_name` varchar(512) DEFAULT NULL COMMENT '商品名称',`commodity_price` varchar(36) DEFAULT '0' COMMENT '商品价格',`number` int(10) DEFAULT '0' COMMENT '商品数量',`description` varchar(2048) DEFAULT '' COMMENT '商品描述',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品信息表';

然后我们在控制台就可以看到如下信息:

如果新增一条数据到表中:

INSERT INTO tb_commodity_info VALUES('3e71a81fd80711eaaed600163e046cc3','熬点','3.99',3,'又ds,老人小孩都喜欢');

控制台可以看到如下信息:

 5、总结

canal的好处在于对业务代码没有侵入,因为是基于监听binlog日志去进行同步数据的。实时性也能做到准实时,其实是很多企业一种比较常见的数据同步的方案。

通过上面的学习之后,我们应该都明白canal是什么,它的原理,还有用法。实际上这仅仅只是入门,因为实际项目中我们不是这样玩的…

实际项目我们是配置MQ模式,配合RocketMQ或者Kafka,canal会把数据发送到MQ的topic中,然后通过消息队列的消费者进行处理。

Canal的部署也是支持集群的,需要配合ZooKeeper进行集群管理。

Canal还有一个简单的Web管理界面。

集群部署Canal,配合使用Kafka,同步数据到Redis

参考资料:Canal官网

这篇关于Canal解决Redis缓存与Mysql数据库的一致性问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/859025

相关文章

Qt开发 , new一个QDialog窗口,点击关闭按钮,内部定义QTimer指针未释放 同时 析构函数也未调用问题

在Qt中,当创建一个QDialog的实例并显示它时,按下关闭按钮(或点击窗口右上角的“X”按钮)会触发窗口的关闭事件,但并不会立即调用其析构函数。这是因为Qt的窗口部件管理内存的方式是基于引用计数的,并且QDialog(以及所有QWidget子类)默认的行为是隐藏窗口而不是删除它。         即使窗口看起来关闭了,QDialog对象仍然存在于内存中,除非显式地删除它。因此,

【SQL Server】入门教程-基础篇(二)

上一篇写的是SQL Server的基础语言,这一篇文章讲的是SQL Server的高级语言。 SQL Server 高级言语学习 LIKE – 模糊查询 LIKE 语法是用来进行对表的模糊查询。 语法: SELECT 列名/(*) FROM 表名称 WHERE 列名称 LIKE 值; 实例: 我们用上一节所用的"Student"学生表来进行演示。 1、现在我们要查询"St

CentOS 删除文件提示 Operation not permitted 的解决方法

1、阿里云服务器提示存在挖矿行为,路径在 /etc/zzh,我们做下删除动作,发现不能删除 [root@MSH etc]# rm -f zzh# 提示rm: cannot remove ‘zzh’: Operation not permitted 2、解决方法: (1)、查看文件权限 [root@MSH etc]# lsattr zzh# 文件只能修改,不能删除----ia-----

MySQL详解--数据库--显示所有数据库

MySQL显示所有数据库 show DATABASES;//显示所有数据库use xxx;//xxx为数据库名show TABLES;//显示xxx下的所有数据表

MySQL详解--数据库--删除

MySQL详解–数据库–删除 show DATABASES;//显示所有数据库DROP DATABASE xxx;//xxx为你的数据库名

Apache Pivot教程 -- 报错解决Error decoding version string “14+36-1461“: For input string: “14+36“

Apache Pivot教程 之前讲到在Apache Pivot运行时可能会报错 Error decoding version string "14+36-1461": For input string: "14+36"Error decoding version string "14+36-1461": For input string: "14+36" 仔细研究后,发现时因为JDK版本

结合源码分析在 Quartz 的集群模式中,比较核心的数据库表是哪些?

目录 引言 核心数据表 1. QRTZ_JOB_DETAILS 2. QRTZ_TRIGGERS 3. QRTZ_CRON_TRIGGERS 4. QRTZ_SIMPLE_TRIGGERS 5. QRTZ_SCHEDULER_STATE 源码中的关系示例 引言         在 Quartz 的集群模式中,核心的数据库表是至关重要的,它们共同协作确保任务调度的信息被正

01背包dp问题

E-来硬的_牛客小白月赛92 (nowcoder.com) 赛时没有看出来,赛后回顾一下值域比较小,有容量有价值,就是一个01背包.. #include <bits/stdc++.h>using namespace std;#define int long longint dp[1000010][2];void solve() {int n, m;cin >> n >> m;memse

如何解决pycharm创建项目报错 Error occurred when installing package ‘requests‘. Details.

🐯 如何解决PyCharm创建项目时的包安装错误:‘requests’ 🛠️ 文章目录 🐯 如何解决PyCharm创建项目时的包安装错误:'requests' 🛠️摘要引言正文📘 **问题分析**🚀 **更换Python版本的详细步骤**1. 确认'requests'支持的Python版本2. 安装适合的Python版本3. 在PyCharm中配置新的Python解释器4. 重

飞书API(6):使用 pandas 处理数据并写入 MySQL 数据库

一、引入 上一篇了解了飞书 28 种数据类型通过接口读取到的数据结构,本文开始探讨如何将这些数据写入 MySQL 数据库。这个工作流的起点是从 API 获取到的一个完整的数据,终点是写入 MySQL 数据表,表结构和维格表结构类似。在过程中可以有不同的工作流程,可以是将接口返回的所有数据作为一个值,直接写入 MySQL 表中,再使用 MySQL 对该值进行解析,处理成不同的列,然后再新建一张表单