【技术派后端篇】canal实现MySQL/Redis缓存一致性
1 前言
在探讨如何利用canal实现MySQL/Redis缓存一致性之前,强烈建议大家先阅读以下几篇相关文章,因为本文是基于这些文章的基础上展开的:
- 《深度剖析 MySQL 与 Redis 缓存一致性:理论、方案与实战》 :该文详细阐述了6种保证缓存一致性的解决方案。为保证数据实时性,文中实战采用了先写缓存MySQL再删除Redis的方式。而本文追求的是最终一致性,采用的是该文中第6种先写MySQL,通过Binlog来异步更新Redis的方案。
- 《使用 Canal 实现 MySQL 与 ES 数据同步的技术实践》 :此篇文章深入讲解了MySQL如何借助Canal中间件将数据同步至ES的原理和实现方案。大家可通过该文理解原理并准备好后续实战所需的环境。这里我们只需要准备好Canal的deployer并使其与MySQL建立通信即可,无需关注整合ES的部分。
- 《Redis分布式锁:原理、实践与应用》 :这篇文章介绍了整合Redis的相关内容,以及如何将Article表中的数据存储至Redis缓存中。本文的代码实战是在该文代码的基础上进行的,大家可重点了解Article表数据存储到Redis缓存的部分以及代码中整合Redis的方式,对于分布式锁部分若暂时没有时间或不理解也无妨,因为本文与分布式锁并无直接关联。
在开始实战前,还需确保前期环境准备工作已完成:
- MySQL正常启动且binlog日志已打开。
- Redis正常启动。
- 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数据并进行处理
- 编写实体类:由于字段与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;
}
- 编写监听处理器:编写代码对Canal监听到的数据进行处理,具体代码路径:
com.github.paicoding.forum.service.article.handler.ArticleHandler
。
/*** 文章详情Handler:mysql——>redis*/
@Slf4j
@Component
@CanalTable("article") // 监听的表名
public class ArticleHandler implements EntryHandler<ArticleDO> {@Autowiredprivate RedisUtil redisUtil;@Overridepublic void insert(ArticleDO articleDO) {log.info("Article表增加数据");}@Overridepublic void update(ArticleDO before, ArticleDO after) {Long articleId = after.getId(); // 获取文章IDthis.delRedisKey(articleId); // 删除Redis的key值log.info("Article表更新数据");}@Overridepublic void delete(ArticleDO articleDO) {Long articleId = articleDO.getId(); // 获取文章IDthis.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 处理器中的 insert
、update
、delete
方法。
4 总结
通过canal实现MySQL/Redis缓存一致性的方案在一定程度上解决了缓存与数据库之间的数据同步问题,但该方案也存在一些缺点。例如,canal-client从Canal中获取数据时一直采用for死循环主动拉取的方式,这会消耗较多的服务性能。为优化这一问题,后期可以考虑让Canal在有数据时采用主动推送的方式,比如引入Kafka中间件,让Canal将数据交给Kafka,然后由Kafka推送给Canal-client,从而避免一直进行for死循环拉取数据的情况,提高系统性能和效率。
希望本文对大家理解和实现MySQL/Redis缓存一致性有所帮助,若在实践过程中有任何疑问或发现文章中有错误之处,欢迎在评论区留言讨论。
5 参考链接
- 技术派Mysql/Redis缓存一致性之Cannal