引言

这篇是《研发应该懂的binlog知识(上)》的下半部分。在本文,我会阐述一下 binlog的结构,以及如何使用 java来解析 binlog。 不过,话说回来,其实严格意义上来说,研发应该还需要懂如何监听binlog的变化。我本来也想写这块的知识,但是后来发现,这块讲起来篇幅过长,需要从 mysql的通讯协议开始讲起,实在是不适合放在这篇文章讲,所以改天抽时间再写一篇监听 binlog变化的文章。

说到这里,大家可能有一个疑问:

研发为什么要懂得如何解析binlog?

说句实在话,如果在实际项目中遇到,我确实推荐使用现成的jar包来解析,比如 mysql-binlog-connector-java或者 open-replicator等。但是呢,这类jar包解析 binlog的原理都是差不多的。因为我有一个怪癖,我用一个jar包,都会去溜几眼,看一下大致原理,所以想在这个部分把如何解析 binlog的实质性原理讲出来,希望大家有所收获。大家懂一个大概的原理即可,不需要自己再去造轮子。另外,注意了,本文教你的是解析 binlog的方法,不可能每一个事件带你解析一遍。能达到举一反三的效果,就是本文的目的。

什么,你还没碰到过解析binlog的需求?没事,那先看着,就当学习一下,将来一定会遇到。

正文

先说一下, binlog的结构。 文件头由一个四字节 MagicNumber构成,其值为1852400382,在内存中就是"0xfe,0x62,0x69,0x6e"。这个 MagicNumber就是来验证这个 binlog文件是否有效 。 引一个题外话

java里头的class文件,头四个字节的Magic Number是多少? 回答:"0xCAFEBABE。"这个数字可能比较难记,记(咖啡宝贝)就好。

下面写个程序,读一份 binlog文件,给大家 binlog看看头四个字节是否为"0xfe,0x62,0x69,0x6e",代码如下

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中的事件,即 EventHeaderEventData。可以理解为下述的对应关系:

  1. EventHeader --> 通用事件头(common-header)

  2. EventData ---> 私有事件头(post-header)和事件体(event-body)

于是,你们可以把Binlog的文件结构像下面这么理解

说一下这个 Checksum,在获取event内容的时候,会增加4个额外字节做校验用。mysql5.6.5以后的版本中binlogchecksum=crc32,而低版本都是binlogchecksum=none。如果不想校验,可以使用set命令设置set binlog_checksum=none。说得再通俗一点, Checksum要么为4个字节,要么为0个字节。

下面说一下通用事件头的结构,如下所示

从上表可以看出,EventHeader固定为19个字节,为此我们构造下面的类,来解析这个通用事件头

public class EventHeader {private long timestamp;private int eventType;private long serverId;private long eventLength;private long nextPosition;private int flags;//省略setter和getter方法@Overridepublic 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,代码如下所示

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=119

nextPosition=123

下一个事件从123字节开始。这是怎么算的呢,当前事件是119字节,算上最开始4个字节的魔数占位符,那么119+4=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/internals/en/format-description-event.html

查询得到 EventData的结构对应如下表所示

ps:这个n其实我们可以推算出,为39。事件长度为119字节,减去事件头19字节,减去末位的4字节(末位四个字节循环校验码),减去2个字节的binlog版本,减去50个字节的服务器版本号,减去4个字节的时间戳,减去1个字节的事件头长度。得到如下算式

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

根据上表结构 ,我们给出一个JAVA类如下所示

public class FormatDescriptionEventData {private int binlogVersion;private String serverVersion;private long timestamp;private int headerLength;private List headerArrays = new ArrayList<Integer>();//省略setter和getter方法@Overridepublic 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();}  }

那如何解析呢,如下所示

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 ormatDescriptionEventData();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/internals/en/format-description-event.html

查询到,该页有一个表格如下所示,其中我红圈的地方,就是私有事件头的长度,即

总结

关于其他事件的结构体,大家可以自行去网站查询,解析原理都是一样的。

研发应该懂的binlog知识(下)相关推荐

  1. mysql binlog_checksum_【原创】研发应该懂的binlog知识(下)

    引言 这篇是<研发应该懂的binlog知识(上)>的下半部分.在本文,我会阐述一下binlog的结构,以及如何使用java来解析binlog. 不过,话说回来,其实严格意义上来说,研发应该 ...

  2. 一图看懂新一代人工智能知识体系大全

    来源:财经头条 摘要:人工智能的发展离不开基础支持层和技术层,基础支持层包括大数据.计算力和算法:技术层包括计算机视觉.语音识别和自然语言处理.人工智能的技术本质是什么,本文会详细分析. 人工智能的发 ...

  3. 前端必须懂的计算机网络知识—(跨域、代理、本地存储)

    前端必须懂的计算机网络知识系列文章: DNS服务器和跨域问题 计算机网络的分层模型 IP地址和MAC地址 前端必须懂的计算机网络知识-(跨域.代理.本地存储) 前端必须懂的计算机网络知识-(TCP) ...

  4. 前端必须懂的计算机网络知识—(XSS、CSRF和HTTPS)

    前端必须懂的计算机网络知识系列文章: DNS服务器和跨域问题 计算机网络的分层模型 IP地址和MAC地址 前端必须懂的计算机网络知识-(跨域.代理.本地存储) 前端必须懂的计算机网络知识-(TCP) ...

  5. 营养素的基础知识下(非技术文)

    营养素的基础知识下 一.碳水化合物(糖类) 1.什么是碳水化合物 2.碳水化合物的分类 3.碳水化合物的作用 4膳食纤维 二.矿物质 1.矿物质的作用 2.矿物质的的特点 3.缺乏矿物质后会产生的后果 ...

  6. 2018.01.27 我懂你的知识焦虑

    阴天  西丽.悦方广场   8house 找个地方消磨周六的时光,这个地方环境挺不错了,有个大大的适合懒懒的躺一下午的大沙发椅 <我懂你的知识焦虑> 第一章  知识焦虑的时代 01  张泉 ...

  7. [0 to 0.5]从零开始学习Android动画知识(下)

    [0 to 0.5]从零开始学习Android动画知识(下) 矢量动画(Scalable Vector Graphics) 不同于前面的为控件做动画效果的方法,矢量动画则是为图形做出动画效果 矢量图 ...

  8. android华为指纹识别开发,华为正在研发“全屏指纹识别+屏下摄像头”共存机型...

    原标题:华为正在研发"全屏指纹识别+屏下摄像头"共存机型 信息显示9月1日中兴将全球首发屏幕下摄像头机型A20 5G,此前业内传言这项技术是华为首发.从现在看来是不可能了,一来中兴 ...

  9. 杨冬敏老师:老板懂点财务知识,看懂三张报表

    杨冬敏老师:老板懂点财务知识,看懂三张报表 很多老板问说开公司老板一定要懂财务相关的知识.如果想要把一家企业经营好,必须要懂得财务的相关知识.如果不懂财务知识的老板,在月末,你问财务说这个月我们公司赚 ...

最新文章

  1. docker 系列之 配置阿里云镜像加速器
  2. matlab在线性系统理论中的应用,线性系统理论相关的matlab应用.ppt
  3. 2019牛客暑期多校训练营(第一场) A Equivalent Prefixes ( st 表 + 二分+分治)
  4. Matplotlib基础(part2)--图形对象
  5. “Python字符串index()方法应用案例”文末三道思考题答案
  6. FPGA入门到精通系列1:数字电路基础知识
  7. Springboot实现阿里云通信短信服务有关短信验证码的发送
  8. 1040. 【GDOI2007】夏娜的菠萝包【推荐】
  9. Unity技术手册 - 生命周期内速度限制(Limit Velocity Over Lifetime)子模块和速度继承(Inherit Velocity)子模块
  10. 《Kotlin 极简教程》第13章 使用 Kotlin 和 Anko 的Android 开发
  11. 用Go构建Teamwork项目的9条教训
  12. 几款比较好用的比较软件
  13. [Android] ListView实现隔行变色(一)
  14. 解决MYSQL 8小时连接问题
  15. Linux紧急救援模式(Centos7)
  16. 首家中国精品店!英国当代珠宝品牌Stephen Webster入驻北京SKP购物中心
  17. 神武2服务器多少级出拍卖系统,快捷购买物品和角色 神武2拍卖系统详解
  18. easypermission坑_Android EasyPermissions官方库高效处理权限相关教程
  19. 解读百度《搜索引擎优化指南》
  20. 一分钟制作精美HTML邮件格式的元旦祝福邮件

热门文章

  1. Jakarta Commons Logging学习笔记
  2. HDU 5821 Ball
  3. 管理员账户遇到“操作需要管理员权限”解决方法
  4. Android中focusable属性的妙用——底层按钮的实现
  5. 语音信号的分帧加窗的matlab实现
  6. 数据库MySQL关系模型之基本概念
  7. Yolov5身份证检测——C++ OpenCV DNN推理
  8. python sftp_Python使用sftp实现上传和下载功能(实例代码)
  9. python深入与提高_Python深入01 特殊方法与多范式
  10. python考试pass or fail_python-pytest学习(十二)-标记失败xfail