数据同步的利器:Canal

欢迎关注微信公众号:互联网全栈架构

在大型应用系统中,往往存在数据异构的现象,比如业务数据保存在MySQL关系数据库中,比如缓存数据放在Redis中,比如支持多维度查询的数据存储在ElasticSearch里面,等等。。。如此种种,不一而足。在这些异构数据之间,我们往往需要把MySQL中的业务数据同步到其他异构数据服务器中,而阿里巴巴推出的一款开源的数据同步工具Canal就可以满足这样的需求。

一、Canal简介

Canal是一款基于MySQL数据库binlog的增量订阅和消费组件,可以使用它来订单数据库的binlog日志,然后进行一些数据消费,基于这种日志增量订阅和消费的业务包括数据库镜像、数据异构、数据索引、缓存更新等。

首先看看MySQL主从复制的原理:

aa6d6e726fa6bf861ee695e4f9240099.png

    1. Master将数据变更写入binlog中。

    2. Slave会订阅Master数据库的binlog,通过I/O线程从binlog拉取日志进行主从同步。

    3. Slave的I/O线程读取到日志后会先写入relay log重放日志中。

    4. Slave的SQL 线程读取relay log进行日志重放,这样就实现了主从数据库间的同步。

而Canal的工作原理就是把自己伪装成MySQL的Slave,模拟MySQL Slave的交互协议向MySQL Master发送dump协议,MySQL Master收到Canal发送过来的dump请求,开始推送binlog给Canal,然后Canal解析binlog,按业务需求进行后续操作,比如同步到其他数据库中等等。

03eeccca4149643036adfb4b722bebe9.png

二、Canal示例

接下来我们看看Canal的具体用法,包括数据库的配置、Java代码编写等。

在MySQL的配置文件my.cnf中添加如下配置:

log-bin=mysql-bin # 开启二进制日志
binglog-format=ROW #使用row模式
server-id=1 #配置主数据库id,不能与从数据库重复

MySQL的主从复制分为三种模式:

Statement,基于语句的复制,在简单来说,就是在主库上执行的SQL,在从库上原样地执行一遍;

Row,基于行的复制,记录每次DML操作导致的数据行变化,并把行的变更同步到从库中;

Mixed,混合模式,根据执行的每一条具体SQL来选择Statement模式或者Rows模式。

使用Canal时建议使用Row模式。

然后创建一个新的MySQL用户,并为其授予查询和复制权限:

CREATE USER 'canal' IDENTIFIED BY 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;

从canal官网下载canal.deployer-1.1.7.tar.gz,解压后在目录conf/example下面有一个文件instance.properties,然后修改里面的数据库相关配置信息,主要是以下几项(IP地址、端口号修改成实际的配置):

# 数据库地址
canal.instance.master.address=MySQL服务器的IP地址:端口号
# 用户名/密码,数据库字符集
canal.instance.dbUsername=canal
canal.instance.dbPassword=canal 
canal.instance.connectionCharset = UTF-8

对于服务器的配置canal.properties,可以先不用修改。

接下来我们写一段示例代码,如果连接的MySQL中数据表数据发生了变化,那么在Java程序监听到这种变化并做相应的业务处理。首先引入Canal客户端的依赖:

<!-- canal 客户端的集成 -->
<dependency>
    <groupId>com.alibaba.otter</groupId>
    <artifactId>canal.client</artifactId>
    <version>1.1.6</version>
</dependency>
<!-- 客户端通信 消息传递 -->
<dependency>
    <groupId>com.alibaba.otter</groupId>
    <artifactId>canal.protocol</artifactId>
    <version>1.1.6</version>
</dependency>

Java代码如下:

package com.sample.canal;

import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import java.net.InetSocketAddress;
import java.util.List;

public class CanalExample {
    public static void main(String[] args) throws Exception {
        CanalConnector canalConnector = CanalConnectors.newSingleConnector(
                new InetSocketAddress("此处为Canal服务地址", 11111), "example", "","");
        canalConnector.connect();
        while (true){
            canalConnector.subscribe();
            Message message = canalConnector.getWithoutAck(100);
            for(CanalEntry.Entry entry: message.getEntries()){
                // 如果是行数据
                if(entry.getEntryType() == CanalEntry.EntryType.ROWDATA){
                    // 解析行变更
                    CanalEntry.RowChange row = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
                    CanalEntry.EventType eventType = row.getEventType();
                    for(CanalEntry.RowData rowData: row.getRowDatasList()){
                        // 如果是新增或者更新数据
                        if(eventType == CanalEntry.EventType.INSERT
                            || eventType == CanalEntry.EventType.UPDATE) {
                            List<CanalEntry.Column> columns = rowData.getAfterColumnsList();
                            // 打印出数据变动情况。此处按业务要求处理
                            printChanges(columns);
                        }
                    }

                }
            }
            canalConnector.ack(message.getId());
        }
    }

    private static void printChanges(List<CanalEntry.Column> columns){
        columns.forEach((column -> {
            String name = column.getName();
            String value = column.getValue();
            System.out.println("列名:"+name+",更新后的值:"+value);
        }));
    }
}

如果我们在数据表中插入一条记录(创建一个很简单的表,只有id和name字段),那么上面的Java程序就会打印出如下的信息:

fe7449eda11568aa15ab9549b51a39c9.png

三、总结

从上面可以看出,Canal的部署和使用也相对比较简单,它把自己伪装成MySQL的Slave,从而可以监听主数据库的变化,这样就方便进行数据同步等操作,当然,它是做一个增量数据的同步,另外,Canal对于业务逻辑没有侵入,在很多需要异构数据同步的场所,它能够大显神威。

创作不易,烦请点赞、分享,感谢!

鸣谢:

https://github.com/alibaba/canal

https://zhuanlan.zhihu.com/p/655011270

推荐阅读:

拒绝空谈:实例演示MySQL事务隔离级别

聊聊MySQL中的死锁

越俎代庖:应用广泛的代理模式

MySQL整数类型的长度到底是什么含义?

漫谈MySQL中的事务

臭名昭著,怙恶不悛的OOM,到底是什么?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值