mysql 协议解析

前言:

以 Mysql 为例,数据库为了主从复制结构和容灾,都会有一份提交日志,通过解析这份日志,理论上说可以获取到每次数据库的数据更新操作。获取这份日志有两种方式:

1、在 MySQL server 上通过外部程序监听磁盘上的 binlog 日志文件
2、借助于 MySQL 的 Master-Slave 结构,使用程序伪装成一个单独的 Slave,通过网络获取到 MySQL 的binlog 日志流
这里有一个注意的点: MySQL 的 binlog 支持三种格式:Statement 、 Row 和 Mixed 格式:

Statement:每一条会修改数据的sql都会记录在binlog中。

优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。(相比row能节约多少性能与日志量,这个取决于应用的SQL情况,正常同一条记录修改或者插入row格式所产生的日志量还小于Statement产生的日志量,但是考虑到如果带条件的update操作,以及整表删除,alter表等操作,ROW格式会产生大量日志,因此在考虑是否使用ROW格式日志时应该跟据应用的实际情况,其所产生的日志量会增加多少,以及带来的IO性能问题。)

缺点:由于记录的只是执行语句,为了这些语句能在slave上正确运行,因此还必须记录每条语句在执行的时候的一些相关信息,以保证所有语句能在slave得到和在master端执行时候相同 的结果。另外mysql 的复制,像一些特定函数功能,slave可与master上要保持一致会有很多相关问题(如sleep()函数, last_insert_id(),以及user-defined functions(udf)会出现问题).

Row:不记录sql语句上下文相关信息,仅保存哪条记录被修改。

优点: binlog中可以不记录执行的sql语句的上下文相关的信息,仅需要记录那一条记录被修改成什么了。所以rowlevel的日志内容会非常清楚的记录下每一行数据修改的细节。而且不会出现某些特定情况下的存储过程,或function,以及trigger的调用和触发无法被正确复制的问题

缺点:所有的执行的语句当记录到日志中的时候,都将以每行记录的修改来记录,这样可能会产生大量的日志内容,比如一条update语句,修改多条记录,则binlog中每一条修改都会有记录,这样造成binlog日志量会很大,特别是当执行alter table之类的语句的时候,由于表结构修改,每条记录都发生改变,那么该表每一条记录都会记录到日志中。

Mixedlevel: 是以上两种level的混合使用,一般的语句修改使用statment格式保存binlog,如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种.新版本的MySQL中队row level模式也被做了优化,并不是所有的修改都会以row level来记录,像遇到表结构变更的时候就会以statement模式来记录。至于update或者delete等修改数据的语句,还是会记录所有行的变更。

由于伪装成 Slave 的解析程序很难像 MySQL slave 一样通过 Master 执行的 SQL 来获取数据更新,因此要将 MySQL Master 的 binlog 格式调整成 Row 格式才方便实现数据更新获取服务。

在 MySQL 中, Master-slave 之间只用标识:

  • serverId:master一般设置为1, 各个 server 之间必须不同
  • binlog 文件名称:当前读取到了哪一个 binlog 文件
  • binlog position:当前读取的 binlog 文件的位置
    由于同步服务会重启,因此必须自行维护 binlog 的状态。一般存储到 MySQL 或者 Zookeeper 中。当服务重启后,自动根据存储的 binlog 位置,继续同步数据。

binlog 解析原理

mysql 主备复制原理
在这里插入图片描述

  • MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)
  • MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
  • MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据

mysql-binlog-connector-java (以下简称mbcj)和 canal 工作原理

  • mbcj 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
  • MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 mbcj)
  • mbcj 解析 binary log 对象(原始为 byte 流)

流程
在这里插入图片描述
1、client 和 DB 握手认证
2、握手成功后,client 对 DB 发送 show master status 命令,此命令将返回当前最新 binlog存储在那个文件以及对应的偏移量。如果想从当前开始接收binglog,则在后面发送binlog dump命令的时候用这两个值就好。
3、发送show global variables like 'binlog_checksum命令,这是由于binlog event发送回来的时候需要,在最后获取event内容的时候,会增加4个额外字节做校验用。mysql5.6.5以后的版本中binlog_checksum=crc32,而低版本都是binlog_checksum=none。如果不想校验,可以使用set命令设置set binlog_checksum=none
4、发送 Dump 命令
Dump命令包图如下所示:
在这里插入图片描述
如上图所示,在报文中塞入binlogPosition和binlogFileName即可让master从相应的位置发送binlog event

MySql-Binlog-Event

一但发送了BinlogDump命令,master就会在数据库有变化的源源不断的推送binlog event到client。

Packet

在mysql中,如果client或server要发送数据,它需要将数据按照(2 * 24 - 1)拆分成packet,给每一个packet添加header,然后再以此发送。
对于一个packet,格式如下:
3 payload length
1 sequence id
string[len] payload

前面3个字节表明的是该packet的长度,每个packet最大不超过16MB。第4个字节表明的是该packet的序列号,从0开始,对于多个packet依次递增,等到下一个新的命令发送数据的时候才重置为0。前面4个字节组成了一个packet的header,后面就是该packet实际的数据。

因为一个packet最大能发送的数据位16MB,所以如果需要发送大于16MB的数据,就需要拆分成多个packet进行发送。

通常,server 会回给 client 三种类型的 packet

  • OK Packet
  • ERROR Packet
  • result set Packets 头包+字段包+EOF包+行包+EOF包

数据类型

mysql协议只有两种基本的数据类型,integer和string。
integer
integer包括fixed length integer和length encoded integer两种,对于length encoded integer,用的地方比较多,这里详细说明一下。
对于一个integer,我们按照如下的方式将其转成length encoded integer:
如果value < 251,使用1 byte
如果value >= 251 同时 value < 2 ** 16,使用fc + 2 byte
如果value >= 2 ** 16 同时 value < 2 ** 24,使用fd + 3 byte
如果value >= 2 ** 24 同时 value < 2 ** 64,使用fe + 8 byte
相应的,对于一个length encoded integer,我们可以通过判断第一个byte的值来转成相应的integer。

string
string包括:
fixed length string,固定长度string
null terminated string,以null结尾的string
variable length string,通过另一个值决定长度的string
length encoded string,通过起始length encoded integer决定长度的string
rest of packet string,从当前位置到包结尾的string

下面再借用一图说明整个交互过程
在这里插入图片描述
mysql 报文外层结构
在这里插入图片描述

  • 前面3字节描述了整个报文中Body的长度,为了解决粘包问题。
  • 第4个字节比较特殊,是为了防止串包用。机制是每收到一个报文都在其sequenceId上加1,并随着需要返回的信息返回回去。如果DB检测到sequenceId连续,则表明没有串包。如果不连续,则串包,DB会直接丢弃这个连接。
  • 补充一点包的最大大小是16M,超过16M,会将剩余的有效负载一起发送额外的数据包,直到数据包的有效负载小于16M

客户端向MySQL发起握手后,MySQL会向客户端发送packet,包括协议版本、MySQL版本、连接id、盐值、身份验证器、MySQL编码index、MySQL能力等,抓包数据如下:
在这里插入图片描述

客户端向MySQL发送对应的AuthPacket认证身份主要包括用户、密码(根据盐值和身份验证器加密)主要分41(SHA-1)和32(SHA-256)两个版本、数据库、编码索引等,抓包数据如下:
在这里插入图片描述
至此就可以进行后续获取 binlog 日志:
1、show global variables like ‘gtid_purged’
2、show master status (返回最新的 binlog 文件名字和位置)
3、建立连接
(1)show global variables like ‘binlog_checksum’ 数据库循环冗余校验
(2) select @@server_id
(3)启用心跳 set @master_heartbeat_period=" + heartbeatInterval * 1000000
4、发送 dump 包

具体协议参考官方网站

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现MySQL协议解析需要对MySQL协议有一定的了解。MySQL协议是基于TCP/IP协议的,它定义了客户端和服务器之间的通信规则。以下是一个简单的C语言实现MySQL协议解析的示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <mysql.h> #define MAX_QUERY_LEN 2048 #define MAX_PACKET_LEN 16777216 int main(int argc, char **argv) { MYSQL mysql; MYSQL_RES *res; MYSQL_ROW row; char query[MAX_QUERY_LEN]; unsigned int packet_len; unsigned char *packet_buf; // 初始化MySQL连接 mysql_init(&mysql); mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "your_prog_name"); // 连接MySQL服务器 if (!mysql_real_connect(&mysql, "localhost", "user", "password", "database", 0, NULL, 0)) { fprintf(stderr, "Failed to connect to database: Error: %s\n", mysql_error(&mysql)); return EXIT_FAILURE; } // 构造查询语句 snprintf(query, sizeof(query), "SELECT * FROM your_table"); // 发送查询请求 if (mysql_real_query(&mysql, query, strlen(query)) != 0) { fprintf(stderr, "Failed to execute query: Error: %s\n", mysql_error(&mysql)); mysql_close(&mysql); return EXIT_FAILURE; } // 接收查询结果 res = mysql_store_result(&mysql); if (res == NULL) { fprintf(stderr, "Failed to store query result: Error: %s\n", mysql_error(&mysql)); mysql_close(&mysql); return EXIT_FAILURE; } // 输出查询结果 while ((row = mysql_fetch_row(res)) != NULL) { printf("%s\t%s\t%s\n", row[0], row[1], row[2]); } // 释放查询结果 mysql_free_result(res); // 关闭MySQL连接 mysql_close(&mysql); return EXIT_SUCCESS; } ``` 在以上示例中,我们使用了MySQL C API来连接MySQL服务器,构造查询语句,发送查询请求,接收查询结果,并输出查询结果。 需要注意的是,以上示例仅是一个简单的MySQL协议解析实现,实际的MySQL协议解析可能会更加复杂,需要根据具体情况进行实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值