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

相关文章

Ubuntu network device not managed 解决方法

今天服务器重装Ubuntu 18.04系统之后,发现有线网卡设备显示network device not managed,用ifconfig也没有显示设备,下面是解决方法,供大家参考。 修改NetworkManager.conf sudo vim /etc/NetworkManager/NetworkManager.conf 将managed改为true: [ifupdown]mana

【MySQL】sql表设计的注意事项

程序员的实用神器 文章目录 程序员的实用神器强烈推荐引言注意事项强烈推荐专栏集锦写在最后 强烈推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站:人工智能 推荐一个个人工作,日常中比较常用的人工智能工具,无需魔法,忍不住分享一下给大家。点击跳转到网站:人工智能工具 引言 在数据库设计中,表设计是至

贪心算法(二)——一般背包问题

题目 有一个背包,最多放M kg的物体(物体大小不限); 有n个物体,每个物体的重量为Wi,每个物体完全放入背包后可获得收益Pi。问:如何放置能获得最大的收益? 注:背包问题分为两种,若每个物体不可分割,则称为0/1背包问题,这种问题无法用贪心法求的最优解,只能求的近似解。而若每个物体可以切分,则称为一般背包问题,可以使用贪心法求的最优解。下面讨论的就是一般背包问题。 结果集

数据库索引全面解析

索引是什么? 数据库中查找操作非常普遍,索引就是提升查找速度的一种手段。 索引分类 B+树索引 它就是传统意义上的索引,它是最常用、最有效的索引。哈希索引 哈希索引是一种自适应的索引,数据库会根据表的使用情况自动生成哈希索引,我们人为是没办法干预的。全文索引 用于实现关键词搜索。但它只能根据空格分词,因此不支持中文。 若要实现搜索功能,可选择lucene。RTree索引 在my

Windows 下制作免安装的 MySQL 简化版

随着 MySQL 版本的升级,安装包的体积越来越大,免安装包更是上百兆。 其实免安装的压缩包中有很多文件是可以省略的,我以免安装包为例来制作一个精简版的 MySQL 。 1、首先下载免安装包 mysql-noinstall-5.1.54-win32.zip(其它版本类似) 2、复制 bin 、data 和 share 三个目录和 my-medium.ini 文件到 d:\tools\

MySQL 第三方客户端工具显示中文乱码

通过第三方工具连接数据库,表中的中文显示为乱码,但是通过 MySQL 的命令行工具却没有问题。 字符集相关变量设置: > SHOW VARIABLES LIKE '%character%'; Variable_name Value ------------------------ ---------------------------- character_set_client ut

关于php 从 5.2 升级到 5.4 后 mantis 报错的问题

mantis 版本 1.1.8,升级 php 版本后页面报错: SYSTEM WARNING: date(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set(

Windows 环境安装 Redis 客户端 phpRedisAdmin

最近在使用 Redis 做为 tomcat 集群中的 session 存储,同时也存储一些访问量比较高的数据。 在调试的过程中,需要查看 Redis 中缓存的数据,默认客户端比较麻烦,最终发现 phpRedisAdmin 是一款不错的东西。 只需要安装 php 的 redis 扩展,但要注意的是 php 的版本号和编译器的版本(vc6 or vc9 or vc11)相对应,这个很重要。

记录接口请求偶发504 Gateway Time-out问题

项目场景: 我们将服务部署到A公司服务器中,使用了共五台服务器,分别是:1.NG服务器 2.日志服务器 3.缓存服务器 4.应用服务器1 5.应用服务器2 。而请求过来首先到达的是他们的物理代理服务器,然后再转发请求到我们的ng服务器,然后再到应用服务器。 问题描述 接口请求经常会网络繁忙,报504 Gateway Time-out,然后返回了个. 原因分析: 网络带宽问题:

linux查看日志,如何解决字符串乱码

主要思路有两种: 【1】本地客户端编码设置错了 如果你用的是putty    一、Linux系统的设置 1)关闭Linux的防火墙 #iptables -F #iptables save 2)编辑/etc/sysconfig/i18n,将LANG=en_US.UTF-8改为LANG=zh_CN.UTF-8 SUPPORT=en_US.UTF-8….也作类似改动,接下来执行: #. /etc/