Mysql binlog 详细解读

Mysql的binlog主要用于逻辑同步以及二阶段提交的安全性保证,而在平时的使用中我们DBA也会从中获取一些重要的信息,比如说主从同步延迟了我们可以通过binlog查看当前事务执行的内容,比如可以利用binlog+备份的方式进行误删回滚,开源工具中也有很多利用binlog做闪回、同步数据到缓存中的方案,异地多活的高可用框架完成后有点空闲就对binlog的组成深入了解了下(基于mysql5.7版本,binlog版本v4),mysql 5.0之后binlog都采用的v4版本,结构如下分为header和data两部分,header部分所有event都一样占用19bytes:

+================================+

| event  | timestamp  0 : 4  |#当前event写入时的时间

| header +-----------------------+

|      | type_code  4 : 1   |#当前event的类型ID

|      +-----------------------+

|      | server_id  5 : 4   |

|      +------------------------+

|      | event_length 9 : 4   |#当前event总字节数

|      +------------------------+

|      | next_position 13 : 4  |#下一个event开始的position

|      +-------------------------+

|      | flags 17 : 2       |#标签

|      +-------------------------+

|      | extra_headers 19 : x-19 |

+===================================+

| event  | fixed part x : y    |

| data  +------------------------+

|      | variable part      |

+=================================+

binlog文件是二进制文件,由一个一个的event组成,每个对数据变动的操作以及DDL语句都会产生一系列的event,:

FORMAT_DESCRIPTION_EVENT:binlog文件的第一个event,记录版本号等元数据信息

QUERY_EVENT: 存储statement类的信息,基于statement的binlog格式记录sql语句,在row模式下记录事务begin标签

XID_EVENT: 二阶段提交xid记录

TABLE_MAP_EVENT: row模式下记录表源数据,对读取行记录提供规则参考,后面会详细介绍

WRITE_ROWS_EVENT/DELETE_ROWS_EVENT/UPDATE_ROWS_EVENT: row模式下记录对应行数据变化的记录

GTID_LOG_EVENT: 这个就是记录GTID事务号了,用于5.6版本之后基于GTID同步的方式

ROTATE_EVENT: 连接下一个binlog文件

Event类型还有很多,而与我们平时操作关联较多的也就上面这几个,有兴趣更详细了解的参考https://dev.mysql.com/doc/internals/en/event-classes-and-types.html

FORMAT_DESCRIPTION_EVENT

简称为format_desc,我们直接在mysql执行reset master清空所有binlog重头生成一个新文件直接操作更容易理解

可以看出format_desc开始的pos位置为4,这是因为每个binlog文件开头都会占用一个固定的4bytes,编码为\xFE\x62\x69\x6E,现在来开始对他进行解析

Format_desc event data部分的fixed part格式分别为

2bytes记录binlog version

50bytes记录MySQL server version

4bytes记录binlog文件创建时间

1bytes 值为19,是所有event的header长度

剩余的所有字节分别记录mysql内部已定义event的fix par部分的长度

按照这个规则可以找到binlog version记录值为0x0004也就是v4,后面50个字节通过解析为5.7.19-log,再4个字节创建时间的时间戳为1503306555,下面是我用python解析可以看出结果

QUERY_EVENT

1. Fixed part部分:

Thread_id: 4bytes 产生数据的线程ID,可以可以用于DBA审计

Execute_time: 4bytes 该语句执行时间,单位秒

Databas_length: 1bytes 库名占字节长度

Error_code: 2bytes 错误代码,一般该值都为0,比如在master上执行inser...select...语句时myisam表出现主键冲突或者innodb表执行途中ctrl+c退出就会记录该值,在slave执行时会检查报错退出

Variable_block_length: 2bytes 记录data part部分variable status的长度

2. Variable part:

Variable_status : Variable_block_length

Database_name: Databas_length

Sql_statement: 整个event剩余部分

TABLE_MAP_EVENT

1. Fixed part:

Table_id : 6bytes

Reserved: 2bytes 预留位置

2. Variable part:

Database_name_length: 1bytes

Database_name: database_name_length + 1个空字节

Table_name_length: 1bytes

Table_name: table_name_length + 1个空字节

Columns: 1bytes 记录表字段数,一般情况字段数是不会超过255所以占1bytes

Columns_type_code: 记录每个字段类型id,每个字段占用1个子节点(colums*1),该位记录的值主要对读取后面源数据提供标准,顺序与表结构字段顺序一致

Metadata_length: 1bytes 字段元数据占用字节长度,同样一般不会超过1bytes

Metadata: 可变长度为metadata_length,需根据Columns_type_code判断每个字段占用长度,数字类型(int,tinyint...)都不会占用空间,只有可变长度的才会占用,比如varchar、char、enum、binary都占用2bytes,text、blob、longtext只占用1bytes,这里的元数据对后面读取row记录提供格式规范

Variable_size: 该部分记录字段是否允许为空,一位代表一个字段,占用字int((N+7/8))bytes,N为字段数

Crc: 4bytes 最后4字节校验码

WRITE_ROWS_EVENT/DELETE_ROWS_EVENT/UPDATE_ROWS_EVENT

1. Fixed part:

Table_id : 6bytes

Reserved: 2bytes 预留位置

Extra: 2bytes 具体干嘛的不清楚,官方文档也没介绍有这2bytes内容

2. Variable part:

Columns: 1bytes 字段数

Variable_size: 可变长度int((n+7)/8),n是字段数,bit标识对应字段是否有值,1代表有,0代表没有,row模式都是有值的

Variable_size: 跟上面一个一样,但是只有update_rows_event才有

Variable_size: 跟上面两个一样计算长度,该值的bit位标识后面所跟的行数据每个字段是否为NULL,为NULL时bit位为0, 1代表有值,这个bit位和table_map_event的columns_type_code顺序对应

Value: 数据内容

Crc: 4bytes 校验码

XID_EVENT

Fixed part为空,只有variable part占有8bytes的xid及结尾的4bytes校验码

GTID_LOG_EVENT

官网未找到有对该event的结构介绍,主要字节分布为1bytes的空位,后面为16bytes的uuid,再有8bytes记录transactionid最后结束

一个正常业务数据库产生的binlog要进行分析的话,基本也就上面这几个和我们关联最多,(https://dev.mysql.com/doc/internals/en/event-data-for-specific-event-types.html)官方文档有对所有event的详细介绍,有兴趣的可以瞧瞧

要对binlog进行解析还需要了解数据存储详细占用情况,同样的在官方文档有详细的介绍,参考https://dev.mysql.com/doc/refman/5.7/en/storage-requirements.html,这里拿我们常用的几个字段类型来做介绍及测试

1. Varchar: 占用字节数0-255bytes使用1byts记录长度,超过255bytes时使用2bytes记录长度 + 数据

2. Int、tinyint、bigint: 分别占用4bytes、1bytes、8bytes

3. Text: 使用2个字节记录长度 + 数据

4. Timestamp(M): 4bytes记录日期时间 + 精确到的毫秒部分,占用长度取决于M

5. Datetime(M):5bytes 记录日期时间 + 精确到的毫秒部分,占用长度取决于M

6. 毫秒部分占用情况,FSP就是上面所说的M值:

FSP    Storage

0,0    0 bytes

1,2    1 bytes

3,4    2bytes

4,5    3bytes

7. Datetime 5bytes记录分布:

1 bit sign (1= non-negative, 0= negative)

17 bits year*13+month (year 0-9999, month 0-12)

5 bits day (0-31)

5 bits hour (0-23)

6 bits minute (0-59)

6 bits second (0-59)

---------------------------

40 bits = 5 bytes

下面我创建了一个包含几个常用字段的表尝试进行对event解析

CREATE TABLE `t1` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`name` varchar(10) DEFAULT NULL,

`content` varchar(256) DEFAULT NULL,

`status` tinyint(4) DEFAULT NULL,

`bignum` bigint(20) DEFAULT NULL,

`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1

插入了一条记录,利用show binlog events找到对应的event的起始点进行解析,先来看table_map

Table_map_event的postion是从302开始的,也就是302的字节位开始,根据上面的介绍我们跳过19bytes的event_header和8bytes的fix par部分,然后的一个字节为0x07表示库名长度为7bytes,接着的7bytes+空结束符就是库名xz_test,接着0x02表示表名长度为2bytes,接着的2bytes+空结束符为表名t1,紧接着的0x06表示表有6个字段,接着的6bytes位分别是6个字段类型id,接着0x05是元数据占用5bytes,从我们创建的表字段顺序来看,首先int、tinyint、bigint不占用字节位,varchar占用2bytes,timesteamp占用1bytes,所以这后面的5个子节点是对应name、content、timesteamp字段的元数据,0x000a是name字段元数据,长度为10,0x0100是content字段长度256,最后的一个字节0x00表示timestamp没有毫秒的精度,最后面5bytes读完该event就结束

紧接着后面的event为write_rows_event是插入的数据所在,首先还是跳过event_header和fix par部分,也就是29个字节,接着的0x06表示有6个字段,接着的0xff的bit位表示各列是否都存在值,这里表示都存在,接下来的0xd0也是用bit位判断,不过这是判断各字段值是否为NULL,0表示null,1表示不为null, 首先4bytes的0x00000002是自增主键id值2,接着是vhachar字段根据table_map得出元数据为10,没有超过255这里占用1bytes即为0x01表示后面的字段内容只占用1bytes,接着的0x61就是插入的’a’,接着就是content字段,根据元数据值得出256大于255所以这里占用2bytes,0x0002数据内容占用2bytes,后面的两字节就是内容,接着是status插入值0x01,紧接着的就是create_time字段值,因为bignum字段我们没有插入值,元数据中没有得出毫秒位精度,所以字节位是0x599baa57,最后4bytes的校验码该event结束

一个事务产生的所有event会被GTID_LOG_EVENT和XID_EVENT包住,table_map和update/delete/write_rows_event的关系通过上面已大概清楚,官方文档中对数据存储及各个event结构都有详细介绍,有兴趣的可以参考官网,用python写了一个对binlog文件分析的脚本,可以快速定位binlog文件中的GTID、thread_id、pos范围、时间范围的数据内容,显示比官方的mysqlbinlog简洁很多(https://github.com/wwwbjqcom/scripts.git),暂时不支持set、bit两个字段类型,显示效果见下图

ps:mysql技术交流qq群479472450,个人的微信公众号也会发一些自己的研究整理的文章,多多关注



时间: 2024-10-15 05:33:47

Mysql binlog 详细解读的相关文章

MemCache超详细解读

MemCache是什么 MemCache是一个自由.源码开放.高性能.分布式的分布式内存对象缓存系统,用于动态Web应用以减轻数据库的负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高了网站访问的速度.MemCaChe是一个存储键值对的HashMap,在内存中对任意的数据(比如字符串.对象等)所使用的key-value存储,数据可以来自数据库调用.API调用,或者页面渲染的结果.MemCache设计理念就是小而强大,它简单的设计促进了快速部署.易于开发并解决面对大规模的数据缓存的

原创工具binlog2sql:从MySQL binlog得到你要的SQL

binlog2sql是我开发的mysql binlog解析工具,它能帮助你从binlog得到你要的SQL.根据不同设置,你可以得到原始SQL.回滚SQL.去除主键的INSERT SQL等. 用途 数据回滚 主从切换后数据不一致的修复 从binlog生成标准SQL,带来的衍生功能 安装 $ git clone https://github.com/danfengcao/binlog2sql.git $ pip install -r requirements.txt 使用 MySQL server必

20180705关于mysql binlog的解析方式

来自:https://blog.csdn.net/u012985132/article/details/74964366/ 关系型数据库和Hadoop生态的沟通越来越密集,时效要求也越来越高.本篇就来调研下实时抓取MySQL更新数据到HDFS. 本篇仅作为调研报告. 初步调研了canal(Ali)+kafka connect+kafka.maxwell(Zendesk)+kafka和mysql_streamer(Yelp)+kafka.这几个工具抓取MySQL的方式都是通过扫描binlog,模拟

Mysql binlog应用场景与原理深度剖析

本文深入介绍Mysql Binlog的应用场景,以及如何与MQ.elasticsearch.redis等组件的保持数据最终一致.最后通过案例深入分析binlog中几乎所有event是如何产生的,作用是什么. 1 基于binlog的主从复制 Mysql 5.0以后,支持通过binary log(二进制日志)以支持主从复制.复制允许将来自一个MySQL数据库服务器(master) 的数据复制到一个或多个其他MySQL数据库服务器(slave),以实现灾难恢复.水平扩展.统计分析.远程数据分发等功能.

Ehcache详细解读

Ehcache详细解读 Ehcache  是现在最流行的纯Java开源缓存框架,配置简单.结构清晰.功能强大,最初知道它,是从Hibernate的缓存开始的.网上中文的EhCache材料以简单介绍和配置方法居多,如果你有这方面的问题,请自行google:对于API,官网上介绍已经非常清楚,请参见官网:但是很少见到特性说明和对实现原理的分析,因此在这篇文章里面,我会详细介绍和分析EhCache的特性,加上一些自己的理解和思考,希望对缓存感兴趣的朋友有所收获. 一.特性一览 ,来自官网,简单翻译一下

mysql binlog row格式查看

MySQL 5.1开始,binlog支持row-based的格式,默认情况下只能看到一些经过base-64编码的信息,如 DELIMITER /*!*/; # at 7493962 #090827 5:25:03 server id 1 end_log_pos 0 Start: binlog v 4, server v 5.1.26-rc-community-log created 090827 5:25:03 BINLOG ' L6iVSg8BAAAAZgAAAAAAAAAAAAQANS4xL

为你详细解读HTTP请求头的具体含意

当我们打开一个网页时,浏览器要向网站服务器发送一个HTTP请求头,然后网站服务器根据HTTP请求头的内容生成当次请求的内容发送给浏览器.你明白HTTP请求头的具体含意吗?下面一条条的为你详细解读,先看某一次HTTP请求头的具体内容: Accept-Language: zh-cn,zh;q=0.5 Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7 Accept: text/html,application/xhtml+xml,application/xml;q=

详细解读PHP类的封装

[导读] public:方法或者属性在任何作用域都可以访问到,而且这是默认的,如果没有为一个属性或方法指定访问修饰符,它将是public.protected:方法或者属性只能从类或者继承类的一个成员中访问到.private:方法或者属 public:方法或者属性在任何作用域都可以访问到,而且这是默认的,如果没有为一个属性或方法指定访问修饰符,它将是public. protected:方法或者属性只能从类或者继承类的一个成员中访问到. private:方法或者属性只能从类的一个成员中访问到,而且无

mysql binlog解析概要

1,dump协议: 根据数据库的ip+port创建socket,如果创建成功,说明链接建立成功,接下来是使用dump协议订阅binlog 链接建立成功之后,服务端会主动向客户端发送如下问候信息greeting(可以理解为经java转换后,是一个java对象), 在下面的代码中可以看到greeting中的信息: this.context.setServerStatus(greeting.getServerStatus());//this.context.setServerVersion(greet