Redis14-缓存同步

news2024/11/16 3:14:41

目录

缓存同步策略

安装Canal

监听Canal


缓存同步策略

缓存数据同步的常见方式有三种:

1.设置有效期:给缓存设置有效期,到期后自动删除,再次查询时更新

  • 优势:简单、方便
  • 缺点:时效性差,缓存过期之前可能不一致
  • 场景:更新频率较低,时效性要求低的业务

2.同步双写:在修改数据库的同时,直接修改缓存

  • 优势:时效性强,缓存与数据库强一致
  • 缺点:有代码侵入,耦合度高;
  • 场景:对一致性、时效性要求较高的缓存数据

3.异步通知:修改数据库时发送事件通知,相关服务监听到通知后修改缓存数据

  • 优势:低耦合,可以同时通知多个缓存服务
  • 缺点:时效性一般,可能存在中间不一致状态
  • 场景:时效性要求一般,有多个服务需要同步

异步通知可以基于MQ或者Canal来实现:

1)基于MQ的异步通知

商品服务完成对数据的修改后,只需要发送一条消息到MQ中,缓存服务监听MQ消息,然后完成对缓存的更新

依然有少量的代码侵入

2)基于Canal的异步通知

商品服务完成商品修改后,业务直接结束,没有任何代码侵入,Canal监听MySQL变化,当发现变化后,立即通知缓存服务,缓存服务接收到canal通知,更新缓存

代码零侵入

安装Canal

Canal [kə'næl],译意为水道/管道/沟渠,Canal是阿里巴巴旗下的一款开源项目,基于Java开发,基于数据库增量日志解析,提供增量数据订阅&消费

GitHub的地址:GitHub - alibaba/canal: 阿里巴巴 MySQL binlog 增量订阅&消费组件

Canal是基于MySQL的主从同步来实现的,MySQL主从同步的原理如下:

  1. MySQL master将数据变更写入二进制日志( binary log),其中记录的数据叫做binary log events

  2. MySQL slave将master的binary log events拷贝到它的中继日志(relay log)

  3. MySQL slave重放relay log中事件,将数据变更反映它自己的数据

Canal就是把自己伪装成MySQL的一个slave节点,从而监听master的binary log变化,再把得到的变化信息通知给Canal的客户端,进而完成对其它数据库的同步

安装步骤:

1.开启MySQL主从

vi /tmp/mysql/conf/my.cnf

添加

log-bin=/var/lib/mysql/mysql-bin
binlog-do-db=heima
  • log-bin=/var/lib/mysql/mysql-bin:设置binary log文件的存放地址和文件名,叫做mysql-bin
  • binlog-do-db=heima:指定对哪个database记录binary log events,这里记录heima这个库

设置用户权限

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

重启mysql容器 

docker restart mysql

2.创建网络,将MySQL、Canal、MQ放到同一个Docker网络中

docker network create heima

让mysql加入

docker network connect heima mysql

3.导入canal的镜像

docker load -i canal.tar

4.创建canal容器

docker run -p 11111:11111 --name canal \
-e canal.destinations=heima \
-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=heima\\..* \
--network heima \
-d canal/canal-server:v1.1.5
  • -p 11111:11111:这是canal的默认监听端口
  • -e canal.instance.master.address=mysql:3306:数据库地址和端口,如果不知道mysql容器地址,可以通过docker inspect 容器id来查看
  • -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

Canal提供了各种语言的客户端,当Canal监听到binlog变化时,会通知Canal的客户端

GitHub上的第三方开源的canal-starter客户端。地址:GitHub - NormanGyllenhaal/canal-client: spring boot canal starter 易用的canal 客户端 canal client

引入依赖:

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

编写配置:

canal:
  destination: heima # canal的集群名字,要与安装canal时设置的名称一致
  server: 192.168.150.101:11111 # canal服务地址

通过@Id、@Column、@Transient注解完成Item与数据库表字段的映射:

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;

import javax.persistence.Column;
import java.util.Date;

@Data
@TableName("tb_item")
public class Item {
    @TableId(type = IdType.AUTO)
    @Id
    private Long id;//商品id
    @Column(name = "name")
    private String name;//商品名称
    private String title;//商品标题
    private Long price;//价格(分)
    private String image;//商品图片
    private String category;//分类名称
    private String brand;//品牌名称
    private String spec;//规格
    private Integer status;//商品状态 1-正常,2-下架
    private Date createTime;//创建时间
    private Date updateTime;//更新时间
    @TableField(exist = false)
    @Transient
    private Integer stock;
    @TableField(exist = false)
    @Transient
    private Integer sold;
}

编写监听器,监听Canal消息:

import com.github.benmanes.caffeine.cache.Cache;
import com.heima.item.config.RedisHandler;
import com.heima.item.pojo.Item;
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_item")
@Component
public class ItemHandler implements EntryHandler<Item> {

    @Autowired
    private RedisHandler redisHandler;
    @Autowired
    private Cache<Long, Item> itemCache;

    @Override
    public void insert(Item item) {
        // 写数据到JVM进程缓存
        itemCache.put(item.getId(), item);
        // 写数据到redis
        redisHandler.saveItem(item);
    }

    @Override
    public void update(Item before, Item after) {
        // 写数据到JVM进程缓存
        itemCache.put(after.getId(), after);
        // 写数据到redis
        redisHandler.saveItem(after);
    }

    @Override
    public void delete(Item item) {
        // 删除数据到JVM进程缓存
        itemCache.invalidate(item.getId());
        // 删除数据到redis
        redisHandler.deleteItemById(item.getId());
    }
}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.heima.item.pojo.Item;
import com.heima.item.pojo.ItemStock;
import com.heima.item.service.IItemService;
import com.heima.item.service.IItemStockService;
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 {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private IItemService itemService;
    @Autowired
    private IItemStockService stockService;

    private static final ObjectMapper MAPPER = new ObjectMapper();

    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化缓存
        // 1.查询商品信息
        List<Item> itemList = itemService.list();
        // 2.放入缓存
        for (Item item : itemList) {
            // 2.1.item序列化为JSON
            String json = MAPPER.writeValueAsString(item);
            // 2.2.存入redis
            redisTemplate.opsForValue().set("item:id:" + item.getId(), json);
        }

        // 3.查询商品库存信息
        List<ItemStock> stockList = stockService.list();
        // 4.放入缓存
        for (ItemStock stock : stockList) {
            // 2.1.item序列化为JSON
            String json = MAPPER.writeValueAsString(stock);
            // 2.2.存入redis
            redisTemplate.opsForValue().set("item:stock:id:" + stock.getId(), json);
        }
    }

    public void saveItem(Item item) {
        try {
            String json = MAPPER.writeValueAsString(item);
            redisTemplate.opsForValue().set("item:id:" + item.getId(), json);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public void deleteItemById(Long id) {
        redisTemplate.delete("item:id:" + id);
    }
}

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

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

相关文章

【CPP】CPP的STL(前篇)

目录 12 STL(前篇)12.1 什么是STL12.2 string类12.2.1 什么是string12.2.2 string类的构造函数12.2.3 string类的析构函数12.2.4 string类的[]重载12.2.5 string类的迭代器的简单了解12.2.6 auto关键字12.2.7 范围for12.2.8 反向迭代器 -- reverse12.2.9 const迭代器12.2.10 CP…

superset定制化配置修改总结

1.需要想用iframe引入dashboard时&#xff0c; URL 参数可用于修改仪表板的呈现方式&#xff0c;standalone0 属性枚举描述standalone0仪表盘正常显示1顶部导航已隐藏2顶部导航 标题被隐藏3顶部导航 标题 顶级标签被隐藏show_filters0渲染没有过滤栏的仪表板1&#xff08;默…

深度学习 —— 个人学习笔记16(目标检测和边界框、目标检测数据集)

声明 本文章为个人学习使用&#xff0c;版面观感若有不适请谅解&#xff0c;文中知识仅代表个人观点&#xff0c;若出现错误&#xff0c;欢迎各位批评指正。 三十二、目标检测和边界框 import torch import matplotlib.pyplot as plt from matplotlib_inline import backend_…

Python爬虫开发:BeautifulSoup、Scrapy入门

在现代网络开发中&#xff0c;网络爬虫是一个非常重要的工具。它可以自动化地从网页中提取数据&#xff0c;并且可以用于各种用途&#xff0c;如数据收集、信息聚合和内容监控等。在Python中&#xff0c;有多个库可以用于爬虫开发&#xff0c;其中BeautifulSoup和Scrapy是两个非…

CVE-2024-38077:Windows远程桌面授权服务的‘隐形杀手’——深度剖析与紧急防护策略

文章目录 CVE-2024-38077&#xff1a;Windows远程桌面授权服务的‘隐形杀手’——深度剖析与紧急防护策略1 漏洞描述2 漏洞影响2.1 处置优先级&#xff1a;高2.2 影响版本 3 漏洞检测3.1 漏洞检测工具3.2 漏洞检测工具使用介绍3.2.1 漏洞检测工具当前支持三种方式检测3.2.2 漏洞…

常见的三个事务问题(脏读/幻读/不可重复读)

常见的三个事务问题&#xff08;脏读/幻读/不可重复读&#xff09; 脏读 脏读&#xff08;Dirty Read&#xff09;是指在一个事务中&#xff0c;读取了另一个未提交事务的数据。 具体来说&#xff0c;脏读的过程如下&#xff1a; 1. 事务A开始&#xff0c;对某一行数据进行…

C++学习笔记之数组

C学习笔记之数组 https://www.runoob.com/cplusplus/cpp-arrays.html C当中&#xff0c;数组是用于存储固定大小的相同类型元素的顺序集合 数组是整体作为一个变量&#xff0c;其中又包含多个单独变量&#xff0c;作为其元素&#xff0c;如数组变量a&#xff0c;其包含a[0]~a[…

html+css+js网页制作 淘宝首页1个页面带js

htmlcssjs网页制作 淘宝首页1个页面带js 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&…

【并查集、树的直径】P2195 HXY造公园 题解

题意 P2195 codeforces 455c&#xff0c;两道一样的题 给出一个由 n n n 个点&#xff0c; m m m 条边组成的森林&#xff0c;有 q q q 组询问,每次询问有以下两种情况 输入 o p 1 op 1 op1 时&#xff1a;给出点 x x x&#xff0c;输出点 x x x 所在的树的直径。 输…

千元不到,作为可穿戴AI设备,AI Friend真的能够取代手机吗?

在人工智能的浪潮中&#xff0c;我们见证了无数旨在提高效率和生产力的创新设备。 然而&#xff0c;Friend设备以其独特的设计理念&#xff0c;为AI设备带来了新的定义——一个永远在线的伴侣&#xff0c;一个情感的稳定器。 一、Friend的设计理念 Friend设备的设计初衷并非追…

vscode的C/C++环境配置和调试技巧

目录 1.背景 2.下载编译器 3.配置环境变量 4.安装C/C插件 5.写C语言代码并且编译成功 5.1文件操作 5.2对于两个窗口的解释 5.3C语言编译环境配置 6.创建执行文件 7.编译运行过程 8.写其他的代码的解决方案一 9.写其他的代码的解决方案二 10.同时编译多个.c文件 10…

Qt 中实现异步散列器

【写在前面】 在很多工作中&#xff0c;我们需要计算数据或者文件的散列值&#xff0c;例如登录或下载文件。 而在 Qt 中&#xff0c;负责这项工作的类为 QCryptographicHash。 关于 QCryptographicHash&#xff1a; QCryptographicHash 是 Qt 框架中提供的一个用于生成加密散列…

【系统维护】Dll文件修复工具使用教程,Windows系统必备!

一、dll文件是什么 dll文件是是一种Windows操作系统下的可执行文件格式&#xff0c;包含可由多个程序同时使用的代码和数据的文件&#xff0c;它的主要作用是实现代码和数据的共享&#xff0c;从而节省内存和硬盘空间&#xff0c;并提高程序的性能和可维护性 二、如何解决dll文…

刚刚,模糊测试平台SFuzz受到行业认可

近日&#xff0c;中国网络安全产业联盟&#xff08;CCIA&#xff09;正式发布了“2024年网络安全优秀创新成果大赛-安全严选专题赛”评选结果&#xff0c;开源网安模糊测试平台SFuzz凭借重大创新能力&#xff0c;得到组委会认可&#xff0c;获本次大赛创新产品优胜奖。 2024年网…

【LeetCode面试150】——392判断子序列

博客昵称&#xff1a;沈小农学编程 作者简介&#xff1a;一名在读硕士&#xff0c;定期更新相关算法面试题&#xff0c;欢迎关注小弟&#xff01; PS&#xff1a;哈喽&#xff01;各位CSDN的uu们&#xff0c;我是你的小弟沈小农&#xff0c;希望我的文章能帮助到你。欢迎大家在…

数据结构--第八天

--哈希表 -哈希表的概念 哈希表&#xff1a;用散列法存储的线性表被称为哈希表&#xff0c;使用的函数被称为散列函数或者哈希函数&#xff0c;f(k)被称为散列地址或者哈希地址。。通常情况下&#xff0c;散列表的储存空间是一个一维数组&#xff0c;而哈希地址为数组的下标 -哈…

【C# WPF】Style全局样式和资源字典

1.全局样式&#xff1a; 在Window.Resource中声明一个样式&#xff0c;总体为白色&#xff0c;为了更有区分度&#xff0c;采用BasedOn这一继承方式来在保留字体和边缘设置的基础上&#xff0c;更改颜色。 <Window x:Class"WpfApp1.Window1"xmlns"http://s…

LangChain 推出 LangGraph Studio:首款用于可视化、交互和调试复杂代理应用的代理 IDE

嘿&#xff0c;听说了吗&#xff1f;Langchain最近发布了一项重大更新&#xff0c;他们推出了官方Agent IDE&#xff0c;并且免费开放了LangGraph平台。这对于AI开发者来说是个好消息&#xff0c;意味着我们现在有了更强大的工具来构建智能应用。 今天&#xff0c;我们就来分享…

CI/CD——CI持续集成实验

目录 一. 安装Docker 二. 部署Jenkins 三. 配置邮箱 四. Harbor部署 五. Nexus Repository部署 五. sonarqube安装 六. 配置Docker 七. jenkins系统配置sonarqube 八. 配置pipeline 九. 构建并集成 一. 安装Docker docker-ce镜像_docker-ce下载地址_docker-ce安装教程…

HTTP、HTTPS、SOCKS5 三种协议特点详解

一、引言 在当今数字化的世界中&#xff0c;网络通信协议扮演着至关重要的角色。HTTP、HTTPS 和 SOCKS5 是三种常见的网络协议&#xff0c;它们各自具有独特的特点和应用场景。本文将对这三种协议进行详细的分析和比较&#xff0c;帮助您更好地理解它们在网络通信中的作用。 …