【技术派后端篇】canal实现MySQL/Redis缓存一致性

1 前言

在探讨如何利用canal实现MySQL/Redis缓存一致性之前,强烈建议大家先阅读以下几篇相关文章,因为本文是基于这些文章的基础上展开的:

  1. 《深度剖析 MySQL 与 Redis 缓存一致性:理论、方案与实战》 :该文详细阐述了6种保证缓存一致性的解决方案。为保证数据实时性,文中实战采用了先写缓存MySQL再删除Redis的方式。而本文追求的是最终一致性,采用的是该文中第6种先写MySQL,通过Binlog来异步更新Redis的方案。
  2. 《使用 Canal 实现 MySQL 与 ES 数据同步的技术实践》 :此篇文章深入讲解了MySQL如何借助Canal中间件将数据同步至ES的原理和实现方案。大家可通过该文理解原理并准备好后续实战所需的环境。这里我们只需要准备好Canal的deployer并使其与MySQL建立通信即可,无需关注整合ES的部分。
  3. 《Redis分布式锁:原理、实践与应用》 :这篇文章介绍了整合Redis的相关内容,以及如何将Article表中的数据存储至Redis缓存中。本文的代码实战是在该文代码的基础上进行的,大家可重点了解Article表数据存储到Redis缓存的部分以及代码中整合Redis的方式,对于分布式锁部分若暂时没有时间或不理解也无妨,因为本文与分布式锁并无直接关联。

在开始实战前,还需确保前期环境准备工作已完成:

  1. MySQL正常启动且binlog日志已打开。
  2. Redis正常启动。
  3. Canal正常启动且能够监听MySQL的binlog日志。

2 SpringBoot整合Canal

  • 项目仓库(GitHub):https://github.com/itwanger/paicoding
  • 项目仓库(码云):https://gitee.com/itwanger/paicoding
  • 分支:origin/feature/mysql_redis_20230610

2.1 引入Canal客户端依赖

在项目中引入Canal客户端依赖,在XML配置文件中添加以下内容:

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

2.2 yml配置文件中配置Canal

在application.yml中进行Canal配置:

# canal配置
canal:
  # canal的地址
  server: 127.0.0.1:11111
  # 默认的数据同步的目的地
  destination: example

需注意,这里的配置要与Canal的配置文件相对应,特别是端口号等信息,务必确保准确无误。配置完成后,可启动项目,检查是否会报Canal连接异常,若项目能正常启动,则说明配置基本没有问题。
在这里插入图片描述

2.3 监听Canal数据并进行处理

  1. 编写实体类:由于字段与mapper层的DO实体类一致,这里直接复用mapper层的DO实体类,无需单独编写。以ArticleDO实体类为例,代码如下:
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("article")
public class ArticleDO extends BaseDO {
    private static final long serialVersionUID = 1L;

    /**
     * 作者
     */
    private Long userId;

    /**
     * 文章类型:1-博文,2-问答
     */
    private Integer articleType;

    /**
     * 文章标题
     */
    private String title;

    /**
     * 短标题
     */
    private String shortTitle;

    /**
     * 文章头图
     */
    private String picture;

    /**
     * 文章摘要
     */
    private String summary;

    /**
     * 类目ID
     */
    private Long categoryId;

    /**
     * 来源:1-转载,2-原创,3-翻译
     *
     * @see SourceTypeEnum
     */
    private Integer source;

    /**
     * 原文地址
     */
    private String sourceUrl;

    /**
     * 状态:0-未发布,1-已发布
     *
     * @see PushStatusEnum
     */
    private Integer status;

    /**
     * 是否官方
     */
    private Integer officalStat;

    /**
     * 是否置顶
     */
    private Integer toppingStat;

    /**
     * 是否加精
     */
    private Integer creamStat;

    private Integer deleted;
}
  1. 编写监听处理器:编写代码对Canal监听到的数据进行处理,具体代码路径:com.github.paicoding.forum.service.article.handler.ArticleHandler
/**
 * 文章详情Handler:mysql——>redis
 */
@Slf4j
@Component
@CanalTable("article") // 监听的表名
public class ArticleHandler implements EntryHandler<ArticleDO> {

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void insert(ArticleDO articleDO) {

        log.info("Article表增加数据");
    }

    @Override
    public void update(ArticleDO before, ArticleDO after) {

        Long articleId = after.getId(); // 获取文章ID
        this.delRedisKey(articleId); // 删除Redis的key值
        log.info("Article表更新数据");
    }

    @Override
    public void delete(ArticleDO articleDO) {

        Long articleId = articleDO.getId(); // 获取文章ID
        this.delRedisKey(articleId); // 删除Redis的key值

        log.info("Article表删除数据");
    }

    private void delRedisKey(Long articleId) {

        String redisCacheKey =
                RedisConstant.REDIS_PAI_DEFAULT
                        + RedisConstant.REDIS_PRE_ARTICLE
                        + RedisConstant.REDIS_CACHE
                        + articleId; // 拼接Redis的key值
        redisUtil.del(redisCacheKey); // 删除Redis的key值
        log.info("删除Redis的key值:" + redisCacheKey);

    }
}

ArticleHandler 类实现了 EntryHandler<ArticleDO> 接口,用于处理 article 相关数据。主要功能是监听 MySQL 数据库中 article 表的数据变更,当数据发生插入、更新或删除操作时,根据情况更新 Redis 中的缓存数据,保证数据库和缓存的数据一致性。

2.4 测试Canal

项目正常启动后,若能看到相关打印日志,则表明项目已正常从Canal中拉取数据。
在这里插入图片描述

2.4.1 数据新增测试

当进行数据新增操作时,可以监听到insert事件,但说明监听功能已正常工作。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.4.2 数据更新测试

对指定id(如103)的数据进行修改操作,进入update方法断点后,可以看到日志在控制台成功打印,同时还能获取到eventType和更新数量等信息。在本次测试中,做了删除Redis缓存的业务逻辑处理,即当监听到update事件时,会在方法中删除对应的Redis缓存。并且可以获取到监听到的before和after数据,以便根据这些数据进行相应的业务处理。
在这里插入图片描述

在这里插入图片描述

2.4.3数据删除测试

执行数据删除操作,进入断点后,控制台会打印相关日志。监听到的数据映射后的articleDO实体也能正确获取,这里根据实体的id做了删除对应缓存的业务处理。
在这里插入图片描述

在这里插入图片描述

3 架构关系

在这里插入图片描述
架构关系图:展示了 MySQL、Canal 和 pai-coding 项目之间的数据流向和交互关系。MySQL 通过 Binlog 日志将数据推送给 Canal,Canal-client 通过死循环从 Canal 拉取数据,然后调用 Handler 处理器中的 insertupdatedelete 方法。

4 总结

通过canal实现MySQL/Redis缓存一致性的方案在一定程度上解决了缓存与数据库之间的数据同步问题,但该方案也存在一些缺点。例如,canal-client从Canal中获取数据时一直采用for死循环主动拉取的方式,这会消耗较多的服务性能。为优化这一问题,后期可以考虑让Canal在有数据时采用主动推送的方式,比如引入Kafka中间件,让Canal将数据交给Kafka,然后由Kafka推送给Canal-client,从而避免一直进行for死循环拉取数据的情况,提高系统性能和效率。

希望本文对大家理解和实现MySQL/Redis缓存一致性有所帮助,若在实践过程中有任何疑问或发现文章中有错误之处,欢迎在评论区留言讨论。

5 参考链接

  1. 技术派Mysql/Redis缓存一致性之Cannal
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值