springboot 集成canal实现redis缓存mysql学习笔记

springboot 集成canal实现redis缓存mysql(单表)

最近在学习如何使用canal来同步mysql数据库,因此记录一下学习使用的流程

1.原理

canal将自己伪装成mysql的slave读取mysql的Binary log实现对mysql数据变动的同步。

在mysql主库日志发生变化后,直接通过canal对redis操作同步mysql的数据,与业务sql数据解耦。

2.配置

mysql配置

版本8.0.16

my.ini设置

[mysqld]
# 设置3306端口
port=3306
# 设置mysql的安装目录
basedir=D:\Environment\mysql-8.0.16-winx64
# 设置mysql数据库的数据的存放目录
datadir=D:\Environment\mysql-8.0.16-winx64\data
# 允许最大连接数
max_connections=200
# 允许连接失败的次数。
max_connect_errors=10
# 服务端使用的字符集默认为UTF8
character-set-server=utf8
# 创建新表时将使用的默认存储引擎
default-storage-engine=INNODB
# 默认使用“mysql_native_password”插件认证
#mysql_native_password
default_authentication_plugin=mysql_native_password
[mysql]
# 设置mysql客户端默认字符集
default-character-set=utf8
[client]
# 设置mysql客户端连接服务端时默认使用的端口
port=3306
default-character-set=utf8
# 开启mysql的binlog模块
log-bin=mysql-bin
binlog-format=ROW
# server_id需保证唯一,不能和canal的slaveId重复
server_id=1
# 需要同步的数据库名称
binlog-do-db=redis_test
# 忽略的数据库,建议填写
binlog-ignore-db=mysql
# 启动mysql时不启动grant-tables授权表
skip-grant-tables

用户授权

CREATE USER canal IDENTIFIED BY 'canal';
grant ALL PRIVILEGES on *.* to 'canal'@'%';
FLUSH PRIVILEGES;

redis配置

版本6.0

#bind 127.0.0.1
protected-mode no
port 6379

canal服务器配置

版本1.1.5

/conf/example/instance.properties

#################################################
## mysql serverId , v1.0.26+ will autoGen 
#不要和mysql主库的server_id相同
canal.instance.mysql.slaveId=2

# enable gtid use true/false
canal.instance.gtidon=false

# position info
#使用了本地的mysql服务器
canal.instance.master.address=127.0.0.1:3306
canal.instance.master.journal.name=
canal.instance.master.position=
canal.instance.master.timestamp=
canal.instance.master.gtid=

# rds oss binlog
canal.instance.rds.accesskey=
canal.instance.rds.secretkey=
canal.instance.rds.instanceId=

# table meta tsdb info
canal.instance.tsdb.enable=false
#canal.instance.tsdb.url=jdbc:mysql://127.0.0.1:3306/canal_tsdb
#canal.instance.tsdb.dbUsername=canal
#canal.instance.tsdb.dbPassword=canal

#canal.instance.standby.address =
#canal.instance.standby.journal.name =
#canal.instance.standby.position =
#canal.instance.standby.timestamp =
#canal.instance.standby.gtid=

# username/password
canal.instance.dbUsername=canal
canal.instance.dbPassword=canal
canal.instance.connectionCharset = UTF-8
# enable druid Decrypt database password
canal.instance.enableDruid=false
#canal.instance.pwdPublicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALK4BUxdDltRRE5/zXpVEVPUgunvscYFtEip3pmLlhrWpacX7y7GCMo2/JM6LeHmiiNdH1FWgGCpUfircSwlWKUCAwEAAQ==

# table regex
canal.instance.filter.regex=.*\\..*
# table black regex
canal.instance.filter.black.regex=mysql\\.slave_.*
# table field filter(format: schema1.tableName1:field1/field2,schema2.tableName2:field1/field2)
#canal.instance.filter.field=test1.t_product:id/subject/keywords,test2.t_company:id/name/contact/ch
# table field black filter(format: schema1.tableName1:field1/field2,schema2.tableName2:field1/field2)
#canal.instance.filter.black.field=test1.t_product:subject/product_image,test2.t_company:id/name/contact/ch

# mq config
canal.mq.topic=example
# dynamic topic route by schema or table regex
#canal.mq.dynamicTopic=mytest1.user,mytest2\\..*,.*\\..*
canal.mq.partition=0
# hash partition config
#canal.mq.partitionsNum=3
#canal.mq.partitionHash=test.table:id^name,.*\\..*
#canal.mq.dynamicTopicPartitionNum=test.*:4,mycanal:6
#################################################

canal启动

启动位置

/bin/startup.sh

关闭位置

/bin/stop.sh

3.java程序

核心canal程序

canal启动后

canal监视mysql日志文件

获得sql语句具体内容并拆解得到rowChage

EventType eventType = rowChage.getEventType();
//数据库名 entry.getHeader().getSchemaName()
//表名 entry.getHeader().getTableName()
//操作名 eventType
//删除操作的数据存放在 rowData.getBeforeColumnsList() 这个列表中
//插入操作的数据存放在 rowData.getAfterColumnsList() 这个列表中
//更新操作则先删除后插入
//每个操作以键值对存储在列表中
//如果有3个字段:id,name,password
//第一个key是id,第二个key是name,第三个key是password
//此列表存储了具体的数据
List<RowData> rowData = rowChage.getRowDatasList();
//通过每次sql操作的eventType判断增删改的类型对rowData中的数据进行个性化处理
//这里使用自定义的redistemplate模板操作数据来讲数据库更改更新至缓存中
//也可以将此处操作替换为自己的,如连接至MQ处理
for (RowData rowData : rowChage.getRowDatasList()) {
                if (eventType == EventType.DELETE) {
                    redisDelete(rowData.getBeforeColumnsList());
                } else if (eventType == EventType.INSERT) {
                    redisInsert(rowData.getAfterColumnsList());
                } else {
                    redisUpdate(rowData.getAfterColumnsList());
                }
            }

自定义redis操作

delete

获得主键字段,删除key为对应id的缓存

private  void redisDelete(List<Column> columns){
    for(Column column:columns){
        if (column.getName().equals("id")){
            redisTemplate.delete("Id::"+column.getValue());
            System.out.println("执行了redisDelete,id="+column.getValue());
        }
    }
}

insert

获得主键字段,set key为主键,value为对象缓存

private  void redisInsert(List<Column> columns){
    User user = new User();
    int insertId=0;
    for (Column column : columns) {
        switch (column.getName()){
            case "id":
                try {
                    insertId=Integer.valueOf(column.getValue()).intValue();
                    user.setId(insertId);
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }
                break;
            case "name":
                user.setName(column.getValue());
                break;
            case "password":
                user.setPassword(column.getValue());
                break;
            default:
                System.out.println("redisInsert,switch结束");
        }
    }
    redisTemplate.opsForValue().set("Id::"+insertId,user);
    System.out.println("执行了redisInsert,id="+insertId);

}

update

获得主键字段,删除key对应的缓存,set key为主键,value为对象缓存/

覆盖该key的value

private  void redisUpdate(List<Column> columns){
    User user = new User();
    int updateId=0;
    for (Column column : columns) {
        switch (column.getName()){
            case "id":
                try {
                    updateId=Integer.valueOf(column.getValue()).intValue();
                    user.setId(updateId);
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }
                break;
            case "name":
                user.setName(column.getValue());
                break;
            case "password":
                user.setPassword(column.getValue());
                break;
            default:
                System.out.println("redisUpdatet,switch结束");
        }
    }
    redisTemplate.opsForValue().set("Id::"+updateId,user);
    System.out.println("执行了redisUpdate,id="+updateId);
}

demo整体架构

mysql

user表,自增主键id(int),name(varchar),password(varchar)

pojo

lombok注解的int id,String name,String password

dao

增删查改

service

增删查改

controller

增删查改

config

RedisConfig

自定义的RedisTemplate<String, Object>

使用jackson和StringRedisSerializer序列化Object和String

SpringCacheRedisConfig

解决cache缓存到redis后的乱码问题

启动类

注入simpleCanalClientExample

bok注解的int id,String name,String password

dao

增删查改

service

增删查改

controller

增删查改

config

RedisConfig

自定义的RedisTemplate<String, Object>

使用jackson和StringRedisSerializer序列化Object和String

SpringCacheRedisConfig

解决cache缓存到redis后的乱码问题

启动类

注入simpleCanalClientExample

作为监视器线程启动

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值