基于binlog+Canal+Redis 数据一致性
基于binlog+Canal+Redis方案是一种解决分布式缓存和数据库之间数据一致性问题的方法,它通过MySQL的binlog和Canal机制,实现数据同步到Redis缓存,以保证数据一致性。
. MySQL主备复制原理
2. MySQL中binlog配置
3. Canal工作原理、安装、配置、使用
4. SpringBoot整合Canal实现数据同步
01
MySQL主备复制原理
MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)
uMySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
uMySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
02
MySQL binlog配置
-
安装mysql(参考安装文档,略过)
-
查看mysql是否开启binlog,ON:开启,OFF:关闭,如果默认已经开启,忽略这一步,查看是否开启命令如下:
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | OFF |
+---------------+-------+
1 row in set (0.00 sec)
-
如果log_bin关闭,修改MySQL配置开启log_bin,步骤如下:
-
修改 mysql 的配置文件 my.cnf
vi /etc/my.cnf
-
追加内容:
server_id=1 #mysql实例id,不能canal的slaveId重复
log-bin=/var/lib/mysql/mysql-bin #binlog文件名
binlog_format=ROW #选择row模式
binlog-do-db=timo #监控的数据库
-
重启mysql
service mysql restart
-
登录 mysql 客户端,查看 log_bin 变量
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON
+---------------+-------+
1 row in set (0.00 sec)
-
mysql数据库发生变化时,在cd /var/lib/mysql 下可以查看mysql-bin.000001文件
cd /var/lib/mysql
03
什么是Canal
canal 是阿里巴巴的一个开源项目,基于java实现,整体已经在很多大型的互联网项目生产环境中使用,包括阿里、美团等都有广泛的应用, 是一个非常成熟的数据库同步方案,基础的使用只需要进行简单的配置即可。canal是通过模拟成为mysql 的slave的方式, 监听mysql 的binlog日志来获取数据,binlog设置为row模式以后,不仅能获取到执行的每一个增删改的脚本,同时还能获取到修改前和修改后的数据,基于这个特性,canal就能高性能的获取到mysql数据数据的变更。
04
Canal工作原理
uCanal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
uMySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
uCanal 解析 binary log 对象(原始为 byte 流)
05
安装和使用Canal
Canal Server下载
官方文档:https://github.com/alibaba/canal/wiki
项目地址:https://github.com/alibaba/canal
下载地址:https://github.com/alibaba/canal/releases
进入下载地址,选择canal.deployer-1.1.5.tar.gz
mkdir /data/canal
cd /data/canal
tar -zxvf /opt/canal.deployer-1.1.5.tar.gz -C ./
解压后的目录如下:
-
- bin # 运维脚本
-
- conf # 配置文件
-
canal_local.properties # canal本地配置,一般不需要动
-
canal.properties # canal服务配置
-
logback.xml # logback日志配置
-
metrics # 度量统计配置
-
spring # spring-实例配置,主要和binlog位置计算、一些策略配置相关,可以在canal.properties选用其中的任意一个配置文件
-
example # 实例配置文件夹,一般认为单个数据库对应一个独立的实例配置文件夹
-
instance.properties # 实例配置,一般指单个数据库的配置
-
- lib # 服务依赖包
-
- logs # 日志文件输出目录
修改内容如下:
在开发和测试环境建议把logback.xml的日志级别修改为DEBUG方便定位问题
canal.mq.topic,这里指定为test,「也就是解析完的binlog结构化数据会发送到Kafka的命名为test的topic中」。canal.mq.partition,这里指定为0。
-
配置工作做好之后,可以启动Canal服务:
-
sh /data/canal/bin/startup.sh
-
# 查看服务日志
-
tail -100f /data/canal/logs/canal/canal.log
-
# 查看实例日志 -- 一般情况下,关注实例日志即可
-
tail -100f /data/canal/logs/example/example.log
06
Spring Boot整合canal实现数据同步
6.1 依赖
-
<!-- canal-spring-boot-starter依赖包 -->
-
<dependency>
-
<groupId>top.javatool</groupId>
-
<artifactId>canal-spring-boot-starter</artifactId>
-
<version>1.2.1-RELEASE</version>
-
</dependency>
6.2 修改配置
此方式需将canal.properties配置文件中的canal.serverMode属性值修改为tcp
canal.mq.topic,这里指定为test,「也就是解析完的binlog结构化数据会发送到Kafka的命名为test的topic中」。canal.mq.partition,这里指定为0。
-
配置工作做好之后,可以启动Canal服务:
-
sh /data/canal/bin/startup.sh
-
# 查看服务日志
-
tail -100f /data/canal/logs/canal/canal.log
-
# 查看实例日志 -- 一般情况下,关注实例日志即可
-
tail -100f /data/canal/logs/example/example.log
06
Spring Boot整合canal实现数据同步
6.1 依赖
-
<!-- canal-spring-boot-starter依赖包 -->
-
<dependency>
-
<groupId>top.javatool</groupId>
-
<artifactId>canal-spring-boot-starter</artifactId>
-
<version>1.2.1-RELEASE</version>
-
</dependency>
6.2 修改配置
此方式需将canal.properties配置文件中的canal.serverMode属性值修改为tcp
6.3 application.yml配置
-
canal:
-
# canal实例名称,要跟canal-server运行时设置destination一致
-
destination: example
-
# canal地址
-
server: 172.16.123.12:11111
6.4 监听binlog的数据 – 订阅数据库增删改操作
-
/**
* 获取canal监听mysql的binlog的数据
*/
// 指定要监听的表
@CanalTable("order")
@Component
@Slf4j
public class MyItemHandler implements EntryHandler<Order> {
@Autowired
private RedisTemplate redisTemplate;
// 监听新增数据,插入redis
@Override
public void insert(Order order) {
log.info("监听新增数据{},插入redis",order);
redisTemplate.opsForValue().set("order:"+order.getOrder_id(), String.valueOf(order));
}
// 监听修改数据,更新redis,更新本地缓存
@Override
public void update(Order before, Order after) {
log.info("监听修改数据{},更新redis,更新本地缓存",after);
redisTemplate.delete("order:"+before.getOrder_id());
redisTemplate.opsForValue().set("order:"+after.getOrder_id(),String.valueOf(after));
}
// 监听删除数据,删除本地缓存,删除redis数据
@Override
public void delete(Order order) {
log.info("监听删除数据,删除本地缓存,删除redis数据",order);
redisTemplate.delete("order:"+order.getOrder_id());
}
}6.5 测试结果