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

使用jackson和StringRedisSerializer序列化Object和String

SpringCacheRedisConfig

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

启动类

注入simpleCanalClientExample

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

dao

增删查改

service

增删查改

controller

增删查改

config

RedisConfig

自定义的RedisTemplate

使用jackson和StringRedisSerializer序列化Object和String

SpringCacheRedisConfig

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

启动类

注入simpleCanalClientExample

作为监视器线程启动

你可能感兴趣的:(mysql,redis)