研发应该懂的binlog知识(下)
引言
这篇是《研发应该懂的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
中的事件,即 EventHeader
和 EventData
。可以理解为下述的对应关系:
EventHeader --> 通用事件头(common-header)
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知识(下)相关推荐
- mysql binlog_checksum_【原创】研发应该懂的binlog知识(下)
引言 这篇是<研发应该懂的binlog知识(上)>的下半部分.在本文,我会阐述一下binlog的结构,以及如何使用java来解析binlog. 不过,话说回来,其实严格意义上来说,研发应该 ...
- 一图看懂新一代人工智能知识体系大全
来源:财经头条 摘要:人工智能的发展离不开基础支持层和技术层,基础支持层包括大数据.计算力和算法:技术层包括计算机视觉.语音识别和自然语言处理.人工智能的技术本质是什么,本文会详细分析. 人工智能的发 ...
- 前端必须懂的计算机网络知识—(跨域、代理、本地存储)
前端必须懂的计算机网络知识系列文章: DNS服务器和跨域问题 计算机网络的分层模型 IP地址和MAC地址 前端必须懂的计算机网络知识-(跨域.代理.本地存储) 前端必须懂的计算机网络知识-(TCP) ...
- 前端必须懂的计算机网络知识—(XSS、CSRF和HTTPS)
前端必须懂的计算机网络知识系列文章: DNS服务器和跨域问题 计算机网络的分层模型 IP地址和MAC地址 前端必须懂的计算机网络知识-(跨域.代理.本地存储) 前端必须懂的计算机网络知识-(TCP) ...
- 营养素的基础知识下(非技术文)
营养素的基础知识下 一.碳水化合物(糖类) 1.什么是碳水化合物 2.碳水化合物的分类 3.碳水化合物的作用 4膳食纤维 二.矿物质 1.矿物质的作用 2.矿物质的的特点 3.缺乏矿物质后会产生的后果 ...
- 2018.01.27 我懂你的知识焦虑
阴天 西丽.悦方广场 8house 找个地方消磨周六的时光,这个地方环境挺不错了,有个大大的适合懒懒的躺一下午的大沙发椅 <我懂你的知识焦虑> 第一章 知识焦虑的时代 01 张泉 ...
- [0 to 0.5]从零开始学习Android动画知识(下)
[0 to 0.5]从零开始学习Android动画知识(下) 矢量动画(Scalable Vector Graphics) 不同于前面的为控件做动画效果的方法,矢量动画则是为图形做出动画效果 矢量图 ...
- android华为指纹识别开发,华为正在研发“全屏指纹识别+屏下摄像头”共存机型...
原标题:华为正在研发"全屏指纹识别+屏下摄像头"共存机型 信息显示9月1日中兴将全球首发屏幕下摄像头机型A20 5G,此前业内传言这项技术是华为首发.从现在看来是不可能了,一来中兴 ...
- 杨冬敏老师:老板懂点财务知识,看懂三张报表
杨冬敏老师:老板懂点财务知识,看懂三张报表 很多老板问说开公司老板一定要懂财务相关的知识.如果想要把一家企业经营好,必须要懂得财务的相关知识.如果不懂财务知识的老板,在月末,你问财务说这个月我们公司赚 ...
最新文章
- docker 系列之 配置阿里云镜像加速器
- matlab在线性系统理论中的应用,线性系统理论相关的matlab应用.ppt
- 2019牛客暑期多校训练营(第一场) A	Equivalent Prefixes ( st 表 + 二分+分治)
- Matplotlib基础(part2)--图形对象
- “Python字符串index()方法应用案例”文末三道思考题答案
- FPGA入门到精通系列1:数字电路基础知识
- Springboot实现阿里云通信短信服务有关短信验证码的发送
- 1040. 【GDOI2007】夏娜的菠萝包【推荐】
- Unity技术手册 - 生命周期内速度限制(Limit Velocity Over Lifetime)子模块和速度继承(Inherit Velocity)子模块
- 《Kotlin 极简教程》第13章 使用 Kotlin 和 Anko 的Android 开发
- 用Go构建Teamwork项目的9条教训
- 几款比较好用的比较软件
- [Android] ListView实现隔行变色(一)
- 解决MYSQL 8小时连接问题
- Linux紧急救援模式(Centos7)
- 首家中国精品店!英国当代珠宝品牌Stephen Webster入驻北京SKP购物中心
- 神武2服务器多少级出拍卖系统,快捷购买物品和角色 神武2拍卖系统详解
- easypermission坑_Android EasyPermissions官方库高效处理权限相关教程
- 解读百度《搜索引擎优化指南》
- 一分钟制作精美HTML邮件格式的元旦祝福邮件
热门文章
- Jakarta Commons Logging学习笔记
- HDU 5821 Ball
- 管理员账户遇到“操作需要管理员权限”解决方法
- Android中focusable属性的妙用——底层按钮的实现
- 语音信号的分帧加窗的matlab实现
- 数据库MySQL关系模型之基本概念
- Yolov5身份证检测——C++ OpenCV DNN推理
- python sftp_Python使用sftp实现上传和下载功能(实例代码)
- python深入与提高_Python深入01 特殊方法与多范式
- python考试pass or fail_python-pytest学习(十二)-标记失败xfail