缓存一致性问题(七)
前面已经在代码中把缓存的增删改操作写好,但是这样写法有一个问题,缓存变更的触发时机是在Mapper方法被调用时才更新的,这个时候问题就出来了,问题就是我们现在把缓存操作都是植入在我们业务逻辑当中的,这个操作导致了耦合性太强,从设计层面来讲其实不太推荐使用这个方法。而且还有一个问题,如果有天业务发生变更,这块业务不需要再存缓存了,那你是不是还得改代码。如果别的业务操作会影响到这一张表数据变动,那也需要对缓存进行处理。再一个不是很可能的情况,如果有人操作机房手动删除了一条数据呢。这就导致了数据一至性的问题。
一、Canal
- 数据库镜像
- 数据库实时备份
- 索引构建和实时维护(拆分异构索引、倒排索引等)
- 业务 cache 刷新
- 带业务逻辑的增量数据处理
二、Canal原理讲解
- MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)
- MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
- MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
- canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump协议
- MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
- canal 解析 binary log 对象(原始为 byte 流)
三、canal安装
1、MySQL开启binlog,对于MySQL , 需要先开启 Binlog 写入功能,配置 binlog-format 为 ROW 模式,my.cnf 中配置如下
docker exec -it mysql /bin/bash cd /etc/mysql/mysql.conf.d vi mysqld.cnf
log-bin=mysql-bin # 开启 binlog binlog-format=ROW # 选择 ROW 模式 server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
docker restart mysql
2、授权 canal 连接 MySQL 账号具有作为 MySQL slave 的权限, 如果已有账户可直接 grant:
CREATE USER canal IDENTIFIED BY 'canal'; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; -- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
FLUSH PRIVILEGES;
3、重启mysql容器
docker restart canal
4、查看是否开启binlog:如果查出的是ON代表开启了
show variables like 'log_bin';
5、Canal安装
docker run -p 11111:11111 --name canal -d docker.io/canal/canal-server
6、进入容器,修改核心配置canal.properties 和instance.properties,canal.properties 是canal自身的配置,instance.properties是需要同步数据的数据库连接配置。
进入canal.properties文件中,如下图添加canal.id=123,123是一个唯一标识,可以随便取
改完这块就进入instance.properties
将文件中的canal.instance.master.address后面的ip改成自己数据库的IP,这块内容意思是连接的数据库
canal.instance.master.address=127.0.0.1:3306
#监听配置
canal.instance.filter.regex=shop_goods.ad_items
配置文件中其它配置解说:
# table regex
#这一句意思是监听任意数据库任意张表 #canal.instance.filter.regex=.*\\..*
docker restart canal
四、缓存同步的实现
有了上面的知识点,下面在spring-cloud-service服务下建立子服务spring-cloud-canal-service来实现缓存同步
POM文件导的包
<dependencies> <!--springboot-canal快速构建依赖包--> <dependency> <groupId>top.javatool</groupId> <artifactId>canal-spring-boot-starter</artifactId> <version>1.2.1-RELEASE</version> </dependency> <dependency> <groupId>com.ghy</groupId> <artifactId>spring-cloud-goods-api</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> </dependencies>
yml配置
server: port: 8083 spring: application: name: spring-cloud-canal-service cloud: nacos: config: file-extension: yaml server-addr: 192.168.32.135:8848 discovery: #Nacos的注册地址 server-addr: 192.168.32.135:8848 #Canal配置 canal: server: 192.168.32.135:11111 destination: example #日志配置 logging: pattern: console: "%msg%n" level: root: error
@Component//交给容器管理 @CanalTable(value = "ad_items")//需要监听的表 public class Monitor implements EntryHandler<AdItems> { @Resource private SkuFeign skuFeign; /**** * 数据库增加数据,执行该方法 * @param adItems */ @Override public void insert(AdItems adItems) { //重新加载缓存 skuFeign.updateTypeItems(adItems.getType()); } /**** * 数据库修改数据,执行该方法 */ @Override public void update(AdItems before, AdItems after) { if(before.getType().intValue()!=after.getType().intValue()){ //重新加载变更前分类的ID对应的推广产品 skuFeign.updateTypeItems(before.getType()); } //重新加载缓存 skuFeign.updateTypeItems(after.getType()); } /**** * 数据库删除数据,执行该方法 */ @Override public void delete(AdItems adItems) { skuFeign.delTypeItems(adItems.getType()); } }
然后我人为在数据库插入一条数据,后台控制台就可以马上收到消息
git源码:https://gitee.com/TongHuaShuShuoWoDeJieJu/spring-cloud-alibaba1.git