zoukankan      html  css  js  c++  java
  • 关于使用Binlog和canal来对MySQL的数据写入进行监控

    先说下Binlog和canal是什么吧。

    1、Binlog是mysql数据库的操作日志,当有发生增删改查操作时,就会在data目录下生成一个log文件,形如mysql-bin.000001,mysql-bin.000002等格式

    2、canal是阿里巴巴旗下的一款开源项目,纯Java开发。基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB);

    3、canal起源:早期,阿里巴巴B2B公司因为存在杭州和美国双机房部署,存在跨机房同步的业务需求。不过早期的数据库同步业务,主要是基于trigger的方式获取增量变更,不过从2010年开始,阿里系公司开始逐步的尝试基于数据库的日志解析,获取增量变更进行同步,由此衍生出了增量订阅&消费的业务,从此开启了一段新纪元。

    4、基于日志增量订阅&消费支持的业务:

    (1)数据库镜像

    (2)数据库实时备份

    (3)多级索引 (卖家和买家各自分库索引)

    (4)search build

    (5)业务cache刷新

    (6)价格变化等重要业务消息

    二、工作原理:

    1、mysql主备复制实现:

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

    (1)master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events,可以通过show binlog events进行查看);

    (2)slave将master的binary log events拷贝到它的中继日志(relay log);

    (3)slave重做中继日志中的事件,将改变反映它自己的数据。

    2、canal的工作原理:

    原理相对比较简单:

    (1)canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议

    (2)mysql master收到dump请求,开始推送binary log给slave(也就是canal)

    (3)canal解析binary log对象(原始为byte流)

    三、主要配置:

    1、mysql默认是没有开启Binlog,不妨查看下本地mysql是否开启,可执行:

    1 SHOW VARIABLES LIKE 'log_%';

    如下图,是我本地mysql,“on”代表已经开启,“off”代表关闭

    2、如何开启Binlog:

    (1)先进入路径为C:ProgramDataMySQLMySQL Server 5.6的文件夹下(如果没有,可能是没有将隐藏的文件夹显示),而不是这个路径C:Program FilesMySQLMySQL Server 5.6

    找到my.ini文件,在打开之前需要先将mysql服务停止,之后在my.ini配置文件中添加以下内容:

     1 #添加这一行就ok  
     2 log-bin=mysql-bin 
     3 #选择row模式 
     4 binlog-format=ROW  
     5 #配置mysql replaction需要定义,不能和canal的slaveId重复  
     6 server_id=1 
     7 
     8 #指定生成log的数据库
     9 binlog_do_db=springboot_test1(这是指定需要生成log的数据库,如果删除这句,则表示所有数据库都需要生成log)
    10 
    11 log-output=FILE
    12 general-log=1  (只需要将0更改为1即可)
    13 general_log_file="MYUNYU.log"
    14 slow-query-log=1
    15 slow_query_log_file="MYUNYU-slow.log"
    16 long_query_time=10

    配置好之后,再启动mysql服务,执行查看binlog是否开启,如果还没开启,那就可能是配置出了问题或者mysql版本的问题,这里不详细说

    (2)从节点通过一个专门的账号连接主节点,这个账号需要拥有全局的 REPLICATION 权限。我们可以使用GRANT 命令创建这样的账号(需要先选中mysql系统库):

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

    再查询mysql系统库中的user表是否存在canal用户:

    3、配置canal:

    首先先下载canal服务端代码canal.deployer-1.1.1.tar.gz(https://github.com/alibaba/canal/releases),解压之后,配置文件在conf文件夹下,

    进入路径为C:...canalcanal.deployer-1.1.1confexample的文件夹,打开配置文件instance.properties,详细配置如下:

     1 #################################################
     2 ## mysql serverId , v1.0.26+ will autoGen 
      #slaveId不能与my.ini配置文件的server_id一致
    3 canal.instance.mysql.slaveId=1234 4 5 # enable gtid use true/false 6 canal.instance.gtidon=false 7 8 # position info 9 canal.instance.master.address=127.0.0.1:3306 10 canal.instance.master.journal.name= 11 canal.instance.master.position= 12 canal.instance.master.timestamp= 13 canal.instance.master.gtid= 14 15 # rds oss binlog 16 canal.instance.rds.accesskey= 17 canal.instance.rds.secretkey= 18 canal.instance.rds.instanceId= 19 20 # table meta tsdb info 21 canal.instance.tsdb.enable=true 22 #canal.instance.tsdb.url=jdbc:mysql://127.0.0.1:3306/springboot_test1 23 #canal.instance.tsdb.dbUsername=canal 24 #canal.instance.tsdb.dbPassword=canal 25 26 #canal.instance.standby.address = 27 #canal.instance.standby.journal.name = 28 #canal.instance.standby.position = 29 #canal.instance.standby.timestamp = 30 #canal.instance.standby.gtid= 31 32 # username/password 33 canal.instance.dbUsername=canal 34 canal.instance.dbPassword=canal 35 canal.instance.connectionCharset = UTF-8
      #这里配置监控的数据库名 36 canal.instance.defaultDatabaseName =springboot_test1 37 # enable druid Decrypt database password 38 canal.instance.enableDruid=false 39 #canal.instance.pwdPublicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALK4BUxdDltRRE5/zXpVEVPUgunvscYFtEip3pmLlhrWpacX7y7GCMo2/JM6LeHmiiNdH1FWgGCpUfircSwlWKUCAwEAAQ== 40 41 # table regex 42 canal.instance.filter.regex=.*\..* 43 # table black regex 44 canal.instance.filter.black.regex= 45 #################################################

    再进入路径为C:...canalcanal.deployer-1.1.1in的文件夹,双击打开startup.bat,如果显示有以下内容,则表示配置成功,canal服务器端启动ok:

    4、客户端代码测试:

    (1)首先创建一个空的maven项目,在pom文件中引入canal客户端的依赖:

    1     <dependency>
    2             <groupId>com.alibaba.otter</groupId>
    3             <artifactId>canal.client</artifactId>
    4             <version>1.0.12</version>
    5         </dependency>

    (2)创建一个类进行测试:

     1 package com.xxx;
     2 
     3 import java.net.InetSocketAddress;
     4 import java.util.List;
     5 
     6 import com.alibaba.otter.canal.client.CanalConnector;
     7 import com.alibaba.otter.canal.protocol.Message;
     8 import com.alibaba.otter.canal.protocol.CanalEntry.Column;
     9 import com.alibaba.otter.canal.protocol.CanalEntry.Entry;
    10 import com.alibaba.otter.canal.protocol.CanalEntry.EntryType;
    11 import com.alibaba.otter.canal.protocol.CanalEntry.EventType;
    12 import com.alibaba.otter.canal.protocol.CanalEntry.RowChange;
    13 import com.alibaba.otter.canal.protocol.CanalEntry.RowData;
    14 import com.alibaba.otter.canal.client.*;
    15 
    16 public class CanalClient {
    17 
    18     public static void main(String args[]) {
    19         // 创建链接            instanceA
    20         CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("127.0.0.1",
    21                 11111), "example", "", "");
    22         int batchSize = 1000;
    23         int emptyCount = 0;
    24         try {
    25             connector.connect();
    26             connector.subscribe(".*\..*");
    27             connector.rollback();
    28             int totalEntryCount = 1200;
    29             while (emptyCount < totalEntryCount) {
    30                 Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
    31                 long batchId = message.getId();
    32                 int size = message.getEntries().size();
    33                 if (batchId == -1 || size == 0) {
    34                     emptyCount++;
    35                     System.out.println("empty count : " + emptyCount);
    36                     try {
    37                         Thread.sleep(1000);
    38                     } catch (InterruptedException e) {
    39                         e.printStackTrace();
    40                     }
    41                 } else {
    42                     emptyCount = 0;
    43                     printEntry(message.getEntries());
    44                 }
    45                 connector.ack(batchId); // 提交确认
    46             }
    47             System.out.println("empty too many times, exit");
    48         }catch (Exception e){
    49             //connector.rollback(batchId); // 处理失败, 回滚数据
    50         }
    51         finally {
    52             connector.disconnect();
    53         }
    54     }
    55 
    56     private static void printEntry( List<Entry> entrys) {
    57         for (Entry entry : entrys) {
    58             if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
    59                 continue;
    60             }
    61             RowChange rowChage = null;
    62             try {
    63                 rowChage = RowChange.parseFrom(entry.getStoreValue());
    64             } catch (Exception e) {
    65                 throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(), e);
    66             }
    67 
    68             EventType eventType = rowChage.getEventType();
    69             System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",
    70                     entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
    71                     entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
    72                     eventType));
    73             for (RowData rowData : rowChage.getRowDatasList()) {
    74                 if (eventType == EventType.DELETE) {
    75                     printColumn(rowData.getBeforeColumnsList());
    76                 } else if (eventType == EventType.INSERT) {
    77                     printColumn(rowData.getAfterColumnsList());
    78 
    79                 } else {
    80                     System.out.println("-------> before");
    81                     printColumn(rowData.getBeforeColumnsList());
    82                     System.out.println("-------> after");
    83                     printColumn(rowData.getAfterColumnsList());
    84                 }
    85             }
    86         }
    87     }
    88 
    89     private static void printColumn( List<Column> columns) {
    90         for (Column column : columns) {
    91             System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
    92         }
    93     }
    94 
    95 }

    运行CanalClient的main方法,如果看到控制台出现以下内容则代表连接成功:

    再到mysql中创建数据库springboot_test,springboot_test1,springboot_test2,再在这三个库中分别创建student表,sql语句为:

    CREATE TABLE `student` (
    `ID` int(11) NOT NULL AUTO_INCREMENT,
    `NAME` varchar(20) NOT NULL,
    `CLASS_NAME` varchar(30) NOT NULL,
    `CREATE_DATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    `UPDATE_DATE` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`ID`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

    执行插入语句:

    1 INSERT INTO student(NAME , class_name )VALUES('student1' , 'class1');

    sql执行完之后再看idea中的控制台,如果出现下面内容打印则表示可以监控数据库的写入等操作

    到此,使用Binlog和canal来对MySQL的数据写入进行监控的操作实现完毕!

  • 相关阅读:
    装箱与拆箱
    java中final的用法
    一次坑爹的Oracle in查询
    Spring-Security-Oauth整合Spring-Security,拦截器
    jvisualvm连接远程Tomcat
    7.Spring-Cloud服务容错保护之Hystrix初探
    8.Spring-Cloud-Hystrix之异常处理
    9.Spring-Cloud-Hystrix之请求缓存(踩坑)
    10.Spring-Cloud-Hystrix之熔断监控Hystrix Dashboard单个应用
    11.Spring-Cloud-Hystrix之熔断监控Turbine
  • 原文地址:https://www.cnblogs.com/mYunYu/p/9935225.html
Copyright © 2011-2022 走看看