缓存数据同步技术Canal

说明:缓存数据同步,以Redis为例,如何保证从Redis中取出来的数据与MySQL中的一致?在微服务架构下,通常可以用以下两种技术来实现:

  • MQ:在修改数据的同时,发送一个消息修改缓存;

在这里插入图片描述

  • Canal:监听数据库,数据库发生改变时,同步更新缓存;

在这里插入图片描述

本文介绍Canal的实现,以下操作均在云服务上,操作系统是CentOS

设置MySQL主从

Canal是基于MySQL的主从同步功能,使用前需要先开启MySQL的主从功能,操作如下:

第一步:开启binlog

找到MySQL容器所挂载的日志文件,添加以下内容:

log-bin=/var/lib/mysql/mysql-bin
binlog-do-db=cache

log-bin=/var/lib/mysql/mysql-bin:指定库记录binary log events,取名为cache;

binlog-do-db=cache:设置binary log文件的存放地址和文件名;

在这里插入图片描述

查看mysql容器所挂载的数据卷可使用下面这个命令

docker volume inspect 数据卷名

如果不知道数据卷名,可停掉mysql容器,并删掉。在/tmp目录下创建一个mysql目录,并进入到mysql目录下,执行下面的命令启动mysql容器;

# 进入/tmp目录
cd /tmp
# 创建文件夹
mkdir mysql
# 进入mysql目录
cd mysql
# 启动mysql容器,设置密码为123456
docker run \-p 3306:3306 \--name mysql \-v $PWD/conf:/etc/mysql/conf.d \-v $PWD/logs:/logs \-v $PWD/data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD=123456 \--privileged \-d \mysql:5.7.25

第二步:设置用户权限

进入mysql命令行模式,如使用navicat或者使用CMD连接MySQL,敲以下命令(建议一行一行敲):

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

在这里插入图片描述

第三部:重启容器

重启mysql容器

在这里插入图片描述

回到mysql命令行,敲下面的命令

show master status;

出现下面的内容,表示设置完成(如果报错了,可能是有延迟,可以等下再敲命令重试)

在这里插入图片描述

环境搭建

第一步:安装Canal

拉取Canal镜像,拉取前应该先去docker官方仓库查看可提供的版本号

docker pull canal:版本号

如果网络状态差()的话不推荐拉取,可使用本地加载的方式;

在这里插入图片描述

加载完成

在这里插入图片描述

第二步:创建网络

canal容器需要和mysql容器关联,创建一个网络,取名demo;

docker network create demo

把mysql容器加入到这个网络中;

docker network connect demo mysql

在这里插入图片描述

第三步:启动canal

输入下面的命令,启动canal容器,需要注意相关名称;

docker run -p 11111:11111 --name canal \
-e canal.destinations=cache \
-e canal.instance.master.address=mysql:3306  \
-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=db_user\\..* \
--network demo \
-d canal/canal-server:v1.1.5

canal.destinations=cache \:binlog-do-db的名称;

canal.instance.master.address=mysql:3306:数据库名称和端口;

--network demo:上面创建的网络名称;

在这里插入图片描述

代码实现

本项目基于Redis缓存预热(参考:http://t.csdn.cn/rZ4En),是一个很简单的项目,部分代码如下:

controller层代码:

@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;/*** 查询所有用户信息* @return*/@GetMapping("list")public List<User> getUsers() {return userService.list();}/*** 根据ID查询用户信息* @param id* @return*/@GetMapping("{id}")public User getUserById(@PathVariable Long id) {return userService.getById(id);}/*** 根据ID删除用户* @param id*/@DeleteMapping("{id}")public void deleteUserById(@PathVariable Long id){userService.removeById(id);}
}

Redis缓存预热代码

@Component
public class RedisHandler implements InitializingBean {@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate UserService userService;private static final ObjectMapper MAPPER = new ObjectMapper();@Overridepublic void afterPropertiesSet() throws Exception {// 初始化缓存// 1.查询所有用户信息List<User> userList = userService.list();// 2.放入缓存for (User user : userList) {// 2.1.将user对象序列化为JSONString json = MAPPER.writeValueAsString(user);// 2.2.设置key前缀,存入redisredisTemplate.opsForValue().set("user:id:" + user.getId(), json);}}
}

因为使用了Redis缓存预热,在项目启动时会使用全查方法,将所有用户的数据存入到redis中;

在这里插入图片描述

第一步:引入依赖

先引入canal的依赖

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

添加相关配置

canal:destination: cache #binlog-do-db的名称;server: 服务器IP:11111

第二步:修改实体类

修改User类,添加一些注解(@Id注解、@Colume注解、@Transient注解),分别用于表示主键,关键字段名,容易发生变动的字段;

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("tb_user")
public class User implements Serializable {@TableId(type = IdType.AUTO)@Idprivate Integer id;@Column(name = "username")private String username;private String password;private String name;private Integer gender;private String image;@Transientprivate Integer job;private String entrydate;@Transientprivate Integer deptId;private String createTime;private String updateTime;@TableLogic(value = "1", delval = "0")private Integer isDel;
}

第三步:修改Redis缓存代码

修改RedisHandler代码如下,添加新增、删除相关的方法;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hzy.pojo.User;
import com.hzy.service.UserService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.util.List;@Component
public class RedisHandler implements InitializingBean {@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate UserService userService;private static final ObjectMapper MAPPER = new ObjectMapper();@Overridepublic void afterPropertiesSet() throws Exception {// 1.查询用户信息List<User> UserList = userService.list();// 2.放入缓存for (User User : UserList) {// 2.1.User序列化为JSONString json = MAPPER.writeValueAsString(User);// 2.2.设置key值,存入redisredisTemplate.opsForValue().set("user:id:" + User.getId(), json);}}public void saveUser(User User) {try {String json = MAPPER.writeValueAsString(User);redisTemplate.opsForValue().set("user:id:" + User.getId(), json);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}public void deleteUserById(Long id) {redisTemplate.delete("user:id:" + id);}
}

第四步:编写监听器类

写一个监听器类,当数据库中的数据发生变化时,会修改Redis中的缓存数据;

import cn.hutool.core.convert.Convert;
import com.github.benmanes.caffeine.cache.Cache;
import com.hzy.config.RedisHandler;
import com.hzy.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import top.javatool.canal.client.annotation.CanalTable;
import top.javatool.canal.client.handler.EntryHandler;@CanalTable("tb_user")
@Component
public class UserHandler implements EntryHandler<User> {@Autowiredprivate RedisHandler redisHandler;@Autowiredprivate Cache<Long, User> userCache;@Overridepublic void insert(User user) {// 写数据到JVM进程缓存userCache.put(Convert.toLong(user.getId()), user);// 写数据到redisredisHandler.saveUser(user);}@Overridepublic void update(User before, User after) {// 写数据到JVM进程缓存userCache.put(Convert.toLong(after.getId()), after);// 写数据到redisredisHandler.saveUser(after);}@Overridepublic void delete(User user) {// 删除数据到JVM进程缓存userCache.invalidate(user.getId());// 删除数据到redisredisHandler.deleteUserById(Convert.toLong(user.getId()));}
}

第五步:启动测试

项目启动后,会发现控制台在实时打印检测状态

在这里插入图片描述

让我们看下Redis中的数据,因为有Redis缓存预热,项目启动就会有所有用户的数据;

在这里插入图片描述

此时,让我们删掉一条用户信息,再查看Redis中的缓存数据,看有没有更新;

在这里插入图片描述

控制台报错了

在这里插入图片描述

百度了说是Druid连接池的问题,我试了下也没有解决

在这里插入图片描述

总之,以上就是缓存数据同步技术Canal的实现,我使用VM测试过,是可以跑通的,可能是云服务器带宽的原因或者是身份验证的原因,导致没有跑通。

另外说一句,即便删除了用户,在Redis缓存那边也还是有用户信息的,因为在User类中设置了“逻辑删除”的字段,所以并不会真的删除用户,但是Redis缓存中对应用户的逻辑删除字段应该是会发生改变的。

删除用户,数据库中对应用户的逻辑删除字段设置为0;

在这里插入图片描述

正常的话,Redis缓存中的该用户信息,逻辑删除字段的值应该要同步修改;

在这里插入图片描述

总结

使用Canal作为缓存数据同步,没有代码入侵,耦合低;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/15204.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSDNmarkdown编辑器文字颜色、大小、字体与背景色的设置

参考原文 007与狼共舞 https://blog.csdn.net/manjianchao/article/details/53668280 一、颜色 浅红色文字&#xff1a;<font color"#dd0000">浅红色文字</font> 深红色文字&#xff1a;<font color"#660000">深红色文字</font>…

Go Ethereum源码学习笔记 001 Geth Start

Go Ethereum源码学习笔记 前言[Chapter_001] 万物的起点: Geth Start什么是 geth&#xff1f;go-ethereum Codebase 结构 Geth Start前奏: Geth Consolegeth 节点是如何启动的NodeNode的关闭 Ethereum Backend附录 前言 首先读者需要具备Go语言基础&#xff0c;至少要通关菜鸟…

对象引用(强,软,弱,虚)

在JDK1.2之前&#xff0c;一个对象只有两种状态"已被引用"和"未被引用" &#xff0c;在JDK1.2后&#xff0c;为了使得程序能够更好的控制对象的生命周期&#xff0c;引入了对象特殊状态的四种引用&#xff0c;由强到弱分别是&#xff1a;强引用&#xff0c…

怎么维护自己的电脑?

方向一&#xff1a;我的电脑介绍 我使用的是一台来自知名品牌的笔记本电脑。它具有高性能的核心配置&#xff0c;如快速处理器、大容量内存和高性能显卡&#xff0c;以及宽敞的存储空间。我选择这台电脑主要是因为它的出色性能和可靠性&#xff0c;能够满足我在学习和工作中的…

【wsl-windows子系统】安装、启用、禁用以及同时支持docker-desktop和vmware方案

如果你要用docker桌面版&#xff0c;很可能会用到wsl&#xff0c;如果没配置好&#xff0c;很可能wsl镜像会占用C盘很多空间。 前提用管理员身份执行 wsl-windows子系统安装和启用 pushd "%~dp0" dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper…

【前端知识】React 基础巩固(三十八)——log、thunk、applyMiddleware中间件的核心代码

React 基础巩固(三十八)——log、thunk、applyMiddleware中间件的核心代码 一、打印日志-中间件核心代码 利用Monkey Patching&#xff0c;修改原有的程序逻辑&#xff0c;在调用dispatch的过程中&#xff0c;通过dispatchAndLog实现日志打印功能 // 打印日志-中间件核心代码…

06. 管理Docker容器数据

目录 1、前言 2、Docker实现数据管理的方式 2.1、数据卷&#xff08;Data Volumes&#xff09; 2.2、数据卷容器&#xff08;Data Volume Containers&#xff09; 3、简单示例 3.1、数据卷示例 3.2、数据卷容器示例 1、前言 在生产环境中使用 Docker&#xff0c;一方面…

【FPGA + 串口】功能完备的串口测试模块,三种模式:自发自收、交叉收发、内源

【FPGA 串口】功能完备的串口测试模块&#xff0c;三种模式&#xff1a;自发自收、交叉收发、内源 VIO 控制单元 wire [1:0] mode;vio_uart UART_VIO (.clk(ad9361_l_clk), // input wire clk.probe_out0(mode) // output wire [1 : 0] probe_out0 );将 mod…

cyber_back

1.1 话题通信 模式&#xff1a; 以发布订阅的方式实现不同节点之间数据交互的通信模式。 如图1-1所示&#xff0c;Listener-Talker通信首先创建了两个Node&#xff0c;分别是Talker Node和 Listener Node。 每个Node实例化Writer类和Reader类对Channel进行消息的读写。 Writer…

211. 添加与搜索单词 - 数据结构设计---------------字典树

211. 添加与搜索单词 - 数据结构设计 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 211. 添加与搜索单词 - 数据结构设计 https://leetcode.cn/problems/design-add-and-search-words-data-structure/descriptio…

Exadata磁盘损坏导致磁盘组无法mount恢复(oracle一体机磁盘组异常恢复)---惜分飞

Oracle Exadata客户,在换盘过程中,cell节点又一块磁盘损坏,导致datac1磁盘组&#xff08;该磁盘组是normal方式冗余)无法mount Thu Jul 20 22:01:21 2023 SQL> alter diskgroup datac1 mount force NOTE: cache registered group DATAC1 number1 incarn0x0728ad12 NOTE: ca…

【iOS】Frame与Bounds的区别详解

iOS的坐标系 iOS特有的坐标是&#xff0c;是在iOS坐标系的左上角为坐标原点&#xff0c;往右为X正方向&#xff0c;向下为Y正方向。 bounds和frame都是属于CGRect类型的结构体&#xff0c;系统的定义如下&#xff0c;包含一个CGPoint&#xff08;起点&#xff09;和一个CGSiz…

[游戏开发][Unity] 打包Xcode工程模拟器+真机调试

本文中会出现的名词有 苹果开发者账号、开发证书(.p12)、开发描述文(.mobileprovision)、发布证书(.p12)、发布描述文(.mobileprovision)、签名、授权电脑、授权iphone手机、 苹果开发者账号 账号分三类&#xff0c;个人&#xff0c;公司&#xff0c;企业&#xff0c;价格99…

windows使用多账户Git,多远程仓库版本管理

1 清除全局配置 git config --global --list // 看一下是否配置过user.name 和 user.email git config --global --unset user.name // 清除全局用户名 git config --global --unset user.email // 清除全局邮箱 2 本地仓库&#xff0c;每个远程对应的本地仓库目录下执行 $…

求三个球面交点的高效解法

文章目录 一、问题描述二、推导步骤代数法几何法 三、MATLAB代码 一、问题描述 如图&#xff0c;已知三个球面的球心坐标分别为 P 1 ( x 1 , y 1 , z 1 ) , P 2 ( x 2 , y 2 , z 2 ) , P 3 ( x 3 , y 3 , z 3 ) P_1(x_1,y_1,z_1),P_2(x_2,y_2,z_2),P_3(x_3,y_3,z_3) P1​(x1​,…

idea项目依赖全部找不到

目录 1&#xff0c;出错现象2&#xff0c;解决3&#xff0c;其他尝试 1&#xff0c;出错现象 很久没打开的Java项目&#xff0c;打开之后大部分依赖都找不到&#xff0c;出现了所有的含有import语句的文件都会报错和一些注解报红报错&#xff0c;但pom文件中改依赖是确实被引入…

深度学习实践——循环神经网络实践

系列实验 深度学习实践——卷积神经网络实践&#xff1a;裂缝识别 深度学习实践——循环神经网络实践 深度学习实践——模型部署优化实践 深度学习实践——模型推理优化练习 代码可见于&#xff1a; 深度学习实践——循环神经网络实践 0 概况1 架构实现1.1 RNN架构1.1.1 RNN架…

java设计模式-工厂模式(下)

接java设计模式-工厂模式&#xff08;上&#xff09; 抽象工厂模式 针对耳机的生产需求&#xff0c;我们可以知道&#xff0c;刚才的工厂已经不满足了&#xff0c;因为只是生产一类产品-手机&#xff0c;但是现在我们需要的工厂类是要生产一个产品族&#xff08;手机和耳机&a…

打卡力扣题目九

#左耳听风 ARST 打卡活动重启# 目录 一、问题 二、解题方法一 三、解题方法二 四、两种方法的区别 关于 ARTS 的释义 —— 每周完成一个 ARTS&#xff1a; ● Algorithm: 每周至少做一个 LeetCode 的算法题 ● Review: 阅读并点评至少一篇英文技术文章 ● Tips: 学习至少一个…

管理类联考——写作——论说文——实战篇——标题篇

角度3——4种材料类型、4个立意对象、5种写作态度 老吕的“1342”&#xff0c;一个标题&#xff0c;三句开头&#xff0c;四层结构&#xff0c;两句结尾。 经过审题立意后&#xff0c;我们要根据我们的立意&#xff0c;确定一个主题&#xff0c;这个主题必须通过文章的标题直接…