工作原理
canal 译意为水道,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。
* MySQL主备复制原理
MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)
MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
* canal 工作原理
canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
canal 解析 binary log 对象(原始为 byte 流)
mysql开启binlog
1. 登录mysql,使用 show variables like 'log_bin'; 命令查看是否已开启binlog
2. 如果没有开启的话,则需要修改my.cnf文件指定binlog信息
# 当前数据库唯一编号
server-id=12345
# 二进制日志存储地址(mysql-bin是文件前缀)
log-bin=/var/lib/mysql/logs/mysql-bin
# binlog日志格式,mysql默认采用statement,canal建议使用ROW
binlog_format=ROW
# binlog过期清理时间
expire_logs_days=7
# binlog每个日志文件大小
max_binlog_size=100m
# binlog缓存大小
binlog_cache_size=4m
# 最大binlog缓存大小
max_binlog_cache_size=512m
这里指定了log-bin=/var/lib/mysql/logs目录,logs是手动创建出来的,且要 chmod -R 777 logs 赋予权限,否则mysql启动失败
3. 修改完成之后,重启mysqld的服务。
canal服务端安装
1. 下载地址
https://github.com/alibaba/canal/releases/tag/canal-1.0.24
2. 解压
mkdir canal && tar -zxvf canal.deployer-1.0.24.tar.gz -C canal
3. 修改pid (canal像一个从库去读取mysql数据,所以他们的id是不能冲突的,mysql的刚才改为12345了)
vim conf/canal.properties
4. 配置数据库信息和监听规则
#################################################
## mysql的id
canal.instance.mysql.slaveId = 12345
# position info
canal.instance.master.address = 127.0.0.1:3306
canal.instance.master.journal.name =
canal.instance.master.position =
canal.instance.master.timestamp =
#canal.instance.standby.address =
#canal.instance.standby.journal.name =
#canal.instance.standby.position =
#canal.instance.standby.timestamp =
# username/password
canal.instance.dbUsername = root
canal.instance.dbPassword = 123456
canal.instance.defaultDatabaseName =
canal.instance.connectionCharset = UTF-8
#所有库的所有表
#canal.instance.filter.regex = .*\..*
#meiye库的system_user表
#canal.instance.filter.regex =meiye.system_user
#meiye 和 brm 的所有表
canal.instance.filter.regex =meiye.*,brm.*
# table black regex
canal.instance.filter.black.regex =
#################################################
~
代码测试
1. 导入依赖 <dependency> <groupId>com.xpand</groupId> <artifactId>starter-canal</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> 2. 配置canal地址信息 canal: client: instances: example: host: 192.168.200.100 port: 11111 batchSize: 1000 3. 启动类开启canal注解 @EnableCanalClient //声明当前的服务是canal的客户端 4. 测试代码 import com.alibaba.otter.canal.protocol.CanalEntry; import com.xpand.starter.canal.annotation.*; /** * mysql数据监听 */ @CanalEventListener public class CanalDataEventListener { /** * 监听新增事件 * @param eventType 操作事件类型(新增) * @param rowData 发生变化的一行数据 */ @InsertListenPoint public void onEventInsert(CanalEntry.EventType eventType, CanalEntry.RowData rowData){ for (CanalEntry.Column column : rowData.getAfterColumnsList()){ System.out.println("列名:"+column.getName()+"----------变化的数据"+column.getValue()); } } /** * 监听修改事件 * @param eventType 操作事件类型(修改) * @param rowData 发生变化的一行数据 */ @UpdateListenPoint public void onEventUpdate(CanalEntry.EventType eventType, CanalEntry.RowData rowData){ for (CanalEntry.Column column : rowData.getBeforeColumnsList()){ System.out.println("修改前列名:"+column.getName()+"----------变化的数据"+column.getValue()); } for (CanalEntry.Column column : rowData.getAfterColumnsList()){ System.out.println("修改后列名:"+column.getName()+"----------变化的数据"+column.getValue()); } } /** * 监听删除事件 * @param eventType 操作事件类型(删除) * @param rowData 发生变化的一行数据 */ @DeleteListenPoint public void onEventDelete(CanalEntry.EventType eventType, CanalEntry.RowData rowData){ for (CanalEntry.Column column : rowData.getBeforeColumnsList()){ System.out.println("删除前的列名:"+column.getName()+"----------变化的数据"+column.getValue()); } } /** * 自定义监听 */ @ListenPoint( eventType = {CanalEntry.EventType.INSERT, CanalEntry.EventType.DELETE, CanalEntry.EventType.UPDATE}, // 监听的事件类型 schema = {"changgou_content"}, // 监听的库 table = {"tb_content"} // 指定监控的表 ) public void onListenPoint(CanalEntry.EventType eventType, CanalEntry.RowData rowData) { for (CanalEntry.Column column : rowData.getBeforeColumnsList()){ System.out.println("自定义操作前:列名:"+column.getName()+"----------变化的数据"+column.getValue()); } for (CanalEntry.Column column : rowData.getAfterColumnsList()){ System.out.println("自定义操作后:列名:"+column.getName()+"----------变化的数据"+column.getValue()); } } }
。