mq同步mysql数据 duplicate entry_mysql数据同步:阿里canal入门浅析

坚持原创,共同进步!请关注我,后续分享更精彩!!!

介绍

canal是阿里开源的一款mysql数据同步工具。纯java开发,基于数据库增量日志解析,提供增量数据的订阅&消费,主要支持mysql。可应用以下业务场景:

  1. 数据库镜像
  2. 数据库实时备份
  3. 多级索引 (卖家和买家各自分库索引)
  4. search build
  5. 业务cache刷新
  6. 价格变化等重要业务消息

工作原理

mysql工作原理:

af68142f4cc3988fd9612a7a2d87900f.png

从上层来看,复制分成三步:

  1. master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events,可以通过show binlog events进行查看);
  2. slave将master的binary log events拷贝到它的中继日志(relay log);
  3. slave重做中继日志中的事件,将改变反映它自己的数据。

canal工作原理:

b7c3dfac9f37258b6f94f738eb21be5a.png
  1. canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
  2. mysql master收到dump请求,开始推送binary log给slave(也就是canal)
  3. canal解析binary log对象(原始为byte流)

通过以上原理,很容易理解canal的数据同步逻辑:就是把canal实例伪装为mysql的一个从库实例,通过mysql自有的数据同步机制来达到数据同步的目的。

2cab6536b4629c2d8dd84e59c94f4557.png

上图可知canal内部设计为server-client的结构。

可把同步数据直接透传到kafka和rocket mq队列。

可通过client端api定制消费mysql同步数据。可也通过adapter api扩展实现HBASE、Rdb、ES的数据同步。

本文通过java client api方式来讲解canal数据消费。

安装准备

自建 MySQL , 需要先开启 Binlog 写入功能,配置 binlog-format 为 ROW 模式,my.cnf 中配置如下

[mysqld]log-bin=mysql-bin # 开启 binlogbinlog-format=ROW # 选择 ROW 模式server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复

注意:针对阿里云 RDS for MySQL , 默认打开了 binlog , 并且账号默认具有 binlog dump 权限 , 不需要任何权限或者 binlog 设置,可以直接跳过这一步

授权 canal 连接 MySQL 账号具有作为 MySQL slave 的权限, 如果已有账户可直接 grant

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

docker安装

canal image tag可参考以下地址:

https://hub.docker.com/r/canal/canal-server/tags/

运行docker容器

docker run -d -p 9100:9100 -p 11110:11110 -p 11111:11111 -p 11112:11112 -e canal.auto.scan=false -e canal.destinations=demo -e canal.instance.master.address=192.168.**.**:3306  -e canal.instance.dbUsername=canal  -e canal.instance.dbPassword=canal  -e canal.instance.connectionCharset=UTF-8 -e canal.instance.tsdb.enable=true -e canal.instance.gtidon=false  -e canal.instance.filter.regex=.*..* -v /data/canal-server/logs:/home/admin/canal-server/logs --restart=always --name=canal-server canal/canal-server:v1.1.4

canal.destinations :canal server实例名。

canal.instance.master.address :mysql主库地址。请确保mysql服务器3306端口对外开放。

canal.instance.dbUsername:mysql主库访问账号

canal.instance.dbPassword:mysql主库访问密码

canal.instance.filter.regex:mysql变更sql过滤规则,支持正则表达式。.*..* 表示所有主库的库和表的变更均会同步。

其他canal配置项可参考官方文档:

https://github.com/alibaba/canal/wiki/AdminGuide

client api

java项目添加依赖

com.alibaba.otter    canal.client    1.1.4

编写测试类

public class CanalTest {    @Test    public void testCanalClient(){        // 创建链接        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.0.114",                11111), "demo", "", "");        int batchSize = 1000;        int emptyCount = 0;        try {            connector.connect();            connector.subscribe(".*..*");            connector.rollback();            int totalEmptyCount = 120;            while (emptyCount < totalEmptyCount) {                Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据                long batchId = message.getId();                int size = message.getEntries().size();                if (batchId == -1 || size == 0) {                    emptyCount++;                    System.out.println("empty count : " + emptyCount);                    try {                        Thread.sleep(1000);                    } catch (InterruptedException e) {                    }                } else {                    emptyCount = 0;                    // System.out.printf("message[batchId=%s,size=%s] ", batchId, size);                    printEntry(message.getEntries());                }                connector.ack(batchId); // 提交确认                // connector.rollback(batchId); // 处理失败, 回滚数据            }            System.out.println("empty too many times, exit");        } finally {            connector.disconnect();        }    }    private static void printEntry(List entrys) {        for (Entry entry : entrys) {            if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {                continue;            }            RowChange rowChage = null;            try {                rowChage = RowChange.parseFrom(entry.getStoreValue());            } catch (Exception e) {                throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),                        e);            }            EventType eventType = rowChage.getEventType();            System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",                    entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),                    entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),                    eventType));            for (RowData rowData : rowChage.getRowDatasList()) {                if (eventType == EventType.DELETE) {                    printColumn(rowData.getBeforeColumnsList());                } else if (eventType == EventType.INSERT) {                    printColumn(rowData.getAfterColumnsList());                } else {                    System.out.println("-------> before");                    printColumn(rowData.getBeforeColumnsList());                    System.out.println("-------> after");                    printColumn(rowData.getAfterColumnsList());                }            }        }    }    private static void printColumn(List columns) {        for (Column column : columns) {            System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());        }    }}

执行test类,新增mysql主库表记录。

8f90951f6b11e648e0da730c6fce643f.png

查看执行结果:

新增的sql数据打印在console面板中,数据的增量更新已同步。

d2ad2ae063e75b5d1d24106b02788dc1.png

总结

本文介绍了canal的原理以及框架结构。通过docker安装canal server实例,并用client api方式消费canal端同步的数据。Canal巧妙的设计,灵活的client api可满足各类数据同步的业务场景。

最后希望本文讲解能给大家以参考和帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值