全球预见者分享平台、领先的数据科学社区媒体和职业发展平台

168大数据

 找回密码
 立即注册

QQ登录

只需一步,快速开始

1 2 3 4 5
开启左侧

研发应该懂的binlog知识

[复制链接]
发表于 2019-1-4 17:04:33 | 显示全部楼层 |阅读模式
本帖最后由 168主编 于 2019-1-4 17:13 编辑

引言为什么写这篇文章?
大家当年在学MySQL的时候,为了能够迅速就业,一般是学习一下MySQL的基本语法,差不多就出山找工作了。水平稍微好一点的童鞋呢还会懂一点存储过程的编写,又或者是懂一点索引的创建和使用。但是呢,基本上大家都忽略了对底层知识的学习。为什么呢?因为工作中很少用到嘛。然后呢,市面上流传的大部分这种底层的知识,又比较偏运维,研发懂这么多意义也不是太大,很多知识可能这辈子都不会用到。
因此,我整理了一部分相关的知识,希望大家有所收获。
研发究竟要懂哪些?
主要分为两个部分
  • binlog的相关概念
  • 怎么解析binlog

计划分上下两个部分来叙述。上部分讲述binlog的相关概念这部分的知识,我们不需要像运维懂的那么深,我会列举一些常见概念和常见配置,大家匆匆扫一眼,有个概念即可。这样大家以后和运维讨论问题的时候,也不会一脸的懵逼。正所谓
懵逼树上懵逼果,懵逼树下你和我。
懵逼树前排排坐,一人一个懵逼果。

博主一个人默默的把懵逼果收走独享就好,各位读者还是懂点基本概念,以后方便和运维沟通。下半部分讲怎么解析binlog
另外,这篇文章是给研发大大看的,可能有些概念我理解的也不对,请运维大大轻喷。
正文
记得我的"一个定义,两个误解,三个用途,四个常识"
一个定义
先从定义开始讲起
binlog是记录所有数据库表结构变更(例如CREATE、ALTER]binlog是记录所有数据库表结构变更(例如CREATE、ALTER]java里头的class文件,头四个字节的Magic Number是多少?
回答:"0xCAFEBABE。"这个数字可能比较难记,记(咖啡宝贝)就好。

下面写个程序,读一份binlog文件,给大家binlog看看头四个字节是否为"0xfe,0x62,0x69,0x6e",代码如下
[AppleScript] 纯文本查看 复制代码
public class MagicParser {
    
    public static final byte[] MAGIC_HEADER = new byte[]{(byte) 0xfe, (byte) 0x62, (byte) 0x69, (byte) 0x6e};
    
    public static void main(String[] args)throws Exception {
        String filePath = "D:\\mysql-bin.000001";
        File binlogFile = new File(filePath);
        ByteArrayInputStream inputStream = null;
        inputStream = new ByteArrayInputStream(new FileInputStream(binlogFile));
        byte[] magicHeader = inputStream.read(4);
        System.out.println("魔数\\xfe\\x62\\x69\\x6e是否正确:"+Arrays.equals(MAGIC_HEADER, magicHeader));
    }
}

输出如下
魔数\xfe\x62\x69\x6e是否正确:true
在文件头之后,跟随的是一个一个事件依次排列。在《binlog二进制文件解析》一文中,将其分为三个部分:通用事件头(common-header)、私有事件头(post-header)和事件体(event-body)。本文修改了一下,只用两个Java类来修饰binlog中的事件,即EventHeader和EventData。可以理解为下述的对应关系:
EventHeader --> 通用事件头(common-header)EventData ---> 私有事件头(post-header)和事件体(event-body)
于是,你们可以把Binlog的文件结构像下面这么理解

说一下这个Checksum,在获取event内容的时候,会增加4个额外字节做校验用。mysql5.6.5以后的版本中binlog_checksum=crc32,而低版本都是binlog_checksum=none。如果不想校验,可以使用set命令设置set binlog_checksum=none。说得再通俗一点,Checksum要么为4个字节,要么为0个字节。
下面说一下通用事件头的结构,如下所示
属性字节数含义
timestamp4包含了该事件的开始执行时间
eventType1事件类型
serverId4标识产生该事件的MySQL服务器的server-id
eventLength4该事件的长度(Header+Data+CheckSum)
nextPosition4下一个事件在binlog文件中的位置
flags2标识产生该事件的MySQL服务器的server-id。
从上表可以看出,EventHeader固定为19个字节,为此我们构造下面的类,来解析这个通用事件头
[AppleScript] 纯文本查看 复制代码
public class EventHeader {
    private long timestamp;
    private int eventType;
    private long serverId;
    private long eventLength;
    private long nextPosition;
    private int flags;
    //省略setter和getter方法
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("EventHeader");
        sb.append("{timestamp=").append(timestamp);
        sb.append(", eventType=").append(eventType);
        sb.append(", serverId=").append(serverId);
        sb.append(", eventLength=").append(eventLength);
        sb.append(", nextPosition=").append(nextPosition);
        sb.append(", flags=").append(flags);
        sb.append('}');
        return sb.toString();
    }
}

OK,接下来,我们来一段代码试着解析一下第一个事件的EventHeader,代码如下所示
[AppleScript] 纯文本查看 复制代码
public class HeaderParser {
    
    public static final byte[] MAGIC_HEADER = new byte[]{(byte) 0xfe, (byte) 0x62, (byte) 0x69, (byte) 0x6e};
    
    public static void main(String[] args)throws Exception {
        String filePath = "D:\\mysql-bin.000001";
        File binlogFile = new File(filePath);
        ByteArrayInputStream inputStream = null;
        inputStream = new ByteArrayInputStream(new FileInputStream(binlogFile));
        byte[] magicHeader = inputStream.read(4);
        if(!Arrays.equals(MAGIC_HEADER, magicHeader)){
            throw new RuntimeException("binlog文件格式不对");
        }
        EventHeader eventHeader = new EventHeader();
        eventHeader.setTimestamp(inputStream.readLong(4) * 1000L);
        eventHeader.setEventType(inputStream.readInteger(1));
        eventHeader.setServerId(inputStream.readLong(4));
        eventHeader.setEventLength(inputStream.readLong(4));
        eventHeader.setNextPosition(inputStream.readLong(4));
        eventHeader.setFlags(inputStream.readInteger(2));       
        System.out.println(eventHeader);
        
    }
}

输出如下
EventHeader{timestamp=1536487335000, eventType=15, serverId=1, eventLength=119, nextPosition=123, flags=1}
注意看,两个参数
eventLength=119nextPosition=123
下一个事件从123字节开始。这是怎么算的呢,当前事件长度是是119字节,算上最开始4个字节的魔数占位符,那么下一个事件自然是,119+4=123,从123字节开始。再强调一次,这个119字节,是包含EventHeader,EventData,Checksum,三个部分的长度为119。
最重要的一个参数
eventType=15
我们去下面的地址
https://dev.mysql.com/doc/internals/en/binlog-event-type.html
查询一下,15对应的事件类型为FORMAT_DESCRIPTION_EVENT。我们接下来,需要知道FORMAT_DESCRIPTION_EVENT所对应EventData的结构。在下面的地址
https://dev.mysql.com/doc/intern ... cription-event.html
查询得到EventData的结构对应如下表所示
属性字节数含义
binlogVersion2binlog版本
serverVersion50服务器版本
timestamp4该字段指明该binlog文件的创建时间。
headerLength1事件头长度,为19
headerArraysn一个数组,标识所有事件的私有事件头的长度
ps:这个n其实我们可以推算出,为39。事件长度为119字节,减去事件头19字节,减去末位的4字节(末位四个字节循环校验码),减去2个字节的binlog版本,减去50个字节的服务器版本号,减去4个字节的时间戳,减去1个字节的事件头长度。得到如下算式
119−19−4−2−50−4−1=39119−19−4−2−50−4−1=39


不过,我们还是假装不知道n是多少吧。

根据上表结构 ,我们给出一个JAVA类如下所示
[AppleScript] 纯文本查看 复制代码
public class FormatDescriptionEventData {
    private int binlogVersion;
    private String serverVersion;
    private long timestamp;
    private int headerLength;
    private List headerArrays = new ArrayList<Integer>();
    //省略setter和getter方法
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("FormatDescriptionEventData");
        sb.append("{binlogVersion=").append(binlogVersion);
        sb.append(", serverVersion=").append(serverVersion);
        sb.append(", timestamp=").append(timestamp);
        sb.append(", headerLength=").append(headerLength);
        sb.append(", headerArrays=").append(headerArrays);
        sb.append('}');
        return sb.toString();
    }   
}

那如何解析呢,如下所示


[AppleScript] 纯文本查看 复制代码
public class HeaderParser {

    public static final byte[] MAGIC_HEADER = new byte[] { (byte) 0xfe,
            (byte) 0x62, (byte) 0x69, (byte) 0x6e };

    public static void main(String[] args) throws Exception {
        String filePath = "D:\\mysql-bin.000001";
        File binlogFile = new File(filePath);
        ByteArrayInputStream inputStream = null;
        inputStream = new ByteArrayInputStream(new FileInputStream(binlogFile));
        byte[] magicHeader = inputStream.read(4);
        if (!Arrays.equals(MAGIC_HEADER, magicHeader)) {
            throw new RuntimeException("binlog文件格式不对");
        }
        EventHeader eventHeader = new EventHeader();
        eventHeader.setTimestamp(inputStream.readLong(4) * 1000L);
        eventHeader.setEventType(inputStream.readInteger(1));
        eventHeader.setServerId(inputStream.readLong(4));
        eventHeader.setEventLength(inputStream.readLong(4));
        eventHeader.setNextPosition(inputStream.readLong(4));
        eventHeader.setFlags(inputStream.readInteger(2));
        System.out.println(eventHeader);
        inputStream.enterBlock((int) (eventHeader.getEventLength() - 19 - 4));
        FormatDescriptionEventData descriptionEventData = new FormatDescriptionEventData();
        descriptionEventData.setBinlogVersion(inputStream.readInteger(2));
        descriptionEventData.setServerVersion(inputStream.readString(50).trim());
        descriptionEventData.setTimestamp(inputStream.readLong(4) * 1000L);
        descriptionEventData.setHeaderLength(inputStream.readInteger(1));
        int sums = inputStream.available();
        for (int i = 0; i < sums; i++) {
            descriptionEventData.getHeaderArrays().add(inputStream.readInteger(1));
        }
        System.out.println(descriptionEventData);
    }
}

至于输出,就不给大家看了,没啥意思。大家看headerArrays的值即可,如下所示
headerArrays=[56, 13, 0, 8, 0, 18, 0, 4, 4, 4, 4, 18, 0, 0, 95, 0, 4, 26, 8, 0, 0, 0, 8, 8, 8, 2, 0, 0, 0, 10, 10, 10, 42, 42, 0, 18, 52, 0, 1]
其实他所输出的值,可以在地址
https://dev.mysql.com/doc/intern ... cription-event.html
查询到,该页有一个表格如下所示,其中我红圈的地方,就是私有事件头的长度,即
总结
关于其他事件的结构体,大家可以自行去网站查询,解析原理都是一样的。

作者:孤独烟 出处: http://rjzheng.cnblogs.com/

楼主热帖
168大数据(www.bi168.cn)是国内首家系统性关注大数据科学与人工智能的社区媒体!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

关于我们|小黑屋|Archiver|168大数据 ( 京ICP备14035423号|申请友情链接

GMT+8, 2019-1-18 14:06 , Processed in 0.115710 second(s), 17 queries , Xcache On.

Powered by BI168社区

© 2012-2014 海鸥科技

快速回复 返回顶部 返回列表