一、canal有什么用
官网对于canal的描述是这样的
canal 译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。
也就是说canal理解为一个用来同步增量数据的一个工具。
官网上的这张图描述了canald的工作原理
canal客户端伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议,之后MySQL会将binary log推送给canal客户端并由客户端进行解析。
二、canal客户端安装
1.配置MySQL开启binary log
修改MySQL的配置信息添加如下内容。
[mysqld]
log-bin=mysql-bin # 开启 binlog
binlog-format=ROW # 选择 ROW 模式
server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
修改完成后重启数据库验证配置是否生效。
show variables like 'log_bin';
二、安装canal
在canal官网进行下载 Releases · alibaba/canal (github.com)
下载完成后进行解压,修改canal配置文件。
conf下初始有一个example文件夹,修改其中的instance.properties文件。
# 配置 slaveId 自定义,不等于 mysql 的 server Id 即可
canal.instance.mysql.slaveId=10
# 数据库地址:自己的数据库ip+端口
canal.instance.master.address=127.0.0.1:3306
# 数据库用户名和密码
canal.instance.dbUsername=
canal.instance.dbPassword=
canal.instance.connectionCharset = UTF-8
# 指定监听库和表,这里的 .* 表示 canal.instance.master.address 下面的所有数据库
canal.instance.filter.regex=
# 配置监听黑名单
canal.instance.filter.black.regex=
canal中regex配置有下面几种
| 表达式 | 范围 | 示例 |
|---|---|---|
| .\.. | 全库全表 | .\.. |
| 库名\..* | 指定库全表 | test\..* |
| 库名.表名 | 指定表 | test.users |
如果多规则组合使用以,分隔。
配置完成后再calal的bin目录中运行startup.sh或者startup.bat即可开启canal客户端。
三、在Java程序中与canal客户端建立连接
1.引入相关依赖
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.7</version>
</dependency>
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.protocol</artifactId>
<version>1.1.6</version>
</dependency>
示例Java代码
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 com.google.protobuf.InvalidProtocolBufferException;
import java.net.InetSocketAddress;
import java.util.List;
public class Main {
private static final int BATCH_SIZE =100;
public static void main(String[] args){
CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("127.0.0.1", 11111), "example", "", "");
try {
connector.connect();
connector.rollback();
while (true){
Message message=connector.getWithoutAck(BATCH_SIZE);
//获取批量ID
long batchId = message.getId();
//获取批量的数量
int size = message.getEntries().size();
//没有新数据休眠0.5ms
if (batchId == -1 || size == 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//获取所有消息
List<CanalEntry.Entry> entries = message.getEntries();
for (CanalEntry.Entry entry : entries) {
//判断消息类型是否为ROWDATA
if (entry.getEntryType().equals(CanalEntry.EntryType.ROWDATA)) {
//获取RowChange对象
CanalEntry.RowChange rowChange= CanalEntry.RowChange.parseFrom(entry.getStoreValue());
System.out.println("事件类型为:"+rowChange.getEventType());
//获取该行所有数据
List<CanalEntry.RowData> rowDatasList = rowChange.getRowDatasList();
System.out.println("修改前数据:");
for(CanalEntry.RowData rowData :rowDatasList){
List<CanalEntry.Column> beforeColumnsList = rowData.getBeforeColumnsList();
for(CanalEntry.Column column:beforeColumnsList){
System.out.println(column.getName()+":"+column.getValue());
}
}
System.out.println("修改后数据:");
for(CanalEntry.RowData rowData :rowDatasList){
List<CanalEntry.Column> afterColumnsList = rowData.getAfterColumnsList();
for(CanalEntry.Column column:afterColumnsList){
System.out.println(column.getName()+":"+column.getValue());
}
}
}
}
connector.ack(batchId); // 提交确认
}
}
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
} finally {
connector.disconnect();
}
}
}
用以下SQL进行测试
insert into test.test values ('a',5,99);
update test.test set score=100 where id=5;
delete from test.test where id=5;
得到输出结果
以上就是canal基本的使用方法。
四、小结
canal的好处在于对业务代码没有侵入,因为是基于监听binlog日志去进行同步数据的。
让基于基于日志增量订阅和消费的业务例如
- 数据库镜像
- 数据库实时备份
- 索引构建和实时维护(拆分异构索引、倒排索引等)
- 业务 cache 刷新
- 带业务逻辑的增量数据处理 更加便捷。