zoukankan      html  css  js  c++  java
  • mysql 协议分析

    MYSQL Binlog协议分析

    此处不讨论建立连接,验证和handshake的交互协议

    Binlog协议

     

    一个MYSQL 通信包由包头包体组成

    包体根据具体的交互协议有自身的组成结构, 在binlog消息体组成结构如下

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

    | event  | timestamp         0 : 4    |

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

    |        | type_code         4 : 1    |

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

    |        | server_id         5 : 4    |

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

    |        | event_length      9 : 4    |

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

    | event  | fixed part       13 : y    |

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

    |        | variable part              |

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

    注意: 消息体里组成是包括2部分的,第一个字节是master发送给slave的errorcode, 第二个字节开始才是具体消息, 所以一个完整的MYSQL binlog通信包组成如下

     

     
       

    一次binlog复制通信包含若干个binlog通信包

    我们通过抓包工具抓取一次binlog复制来分析

     

     

    我们在master里更新了一条数据, 产生了 41 这条通信数据, 里面包含了4个mysql binlog通信包

    第一个binlog事件消息

    头3个字节 45 00 00就是消息长度, 注意,在mysql协议里,数值类型是用小序列来传输数据的(小序列: 低位先传), 这条消息里实际消息长度内容是 00 00 45 , 也就是69(45是16进制, 69是10进制)

    第4个字节2c是master传过来的序号

    第5个字节00时errorcode, 0表示没有错误, 其他是错误

    第6~9  4个字节是timestamp

    第10 个字节02 是event type, 比如query, insert, update,delete等的类型码

    第11-14个字节是serverid, 这里是02 00 00 00, 也是小序列

    第15~19个字节是eventlengthoffset, 这里是44 00 00 00, 也是小序列

    第二个binlog事件消息

     

    结构和第一个基本一样, 只是消息长度,序号,eventtype等内容不一样

    以上是原生mysql复制binlog的交互协议情况, 如果开启了semi半同步, 协议就发生变化, tcp通信次数, 包体结构等都和原来的有差异

    抓包分析:

     

    我们在master里更新了一条数据, 产生了 5 这条通信数据, 里面包含了5个mysql binlog通信包, 比原生的多了1条, 而且包结构也变成了以下结构

     

     
       

    第一个binlog事件消息是新增的事件

     

    第6个字节是新增的字节码, ef是固定的值,表示这是个semi的消息

    第7个字节是新增的字节码, 0或者1, 0表示不需要回一个ack给master, 1表示需要, 一次复制通信(包含5个binlog事件)只有最后一个binlog事件的值为1

    第8个字节开始就是具体消息, 和原生的一样

    最后一个binlog事件消息

     

    第7个字节值是1 ,  表示要返回一个ack给master

    以下就是ack给master的内容

     

    Slave ack给master 的消息是7这条通信数据

    第1-3个字节师消息长度,这里的内容是12 00 00, 十进制内容是18

    第4个字节是序号 , 这里是0, (注意: 每次tcp交互里这个序号都被重至为0)

    第5个字节是semi标示 ,固定为ef

    第6~14个字节,一共8个字节表示位置offset, ef 0a 00 00 00 00 00 00 同样是小序列

    第15个字节开始是binlog的文件名 每个字节存储的是ascii码, 如这里的4f=O, 4e=N,2e=.,30=0,36=6, 就是ON.000006

    如果安装了semi插件后,并不启用semi同步,就不会发送ack给master, 就是上面7这个数据包不会发送

    Master要发送semi字节给slave前提是slave连接上master后必须发送SET @rpl_semi_sync_slave= 1 指令给master, 看下面抓包效果

     

    1c 00 00 00 是包头

    0x03 是指令码       COM_QUERY

    53开始后面的字节都是具体字符串的ascii码

    完整事件流

    Slave连接上master后, master 首先会发ROTATE_EVENT和FORMAT_DESCRIPTION_EVENT 2个事件给slave, ROTATE_EVENT事件告诉slave下一个要读取的binlog(可以理解成初始化要读取的binlog)

    Master发生一个插入sql, 如insert into test1 values(15), 分别发送给slave的事件是ANONYMOUS_GTID_LOG_EVENT-->QUERY_EVENT-->TABLE_MAP_EVENT-->WRITE_ROWS_EVENT-->XID_EVENT

    (1)

    ANONYMOUS_GTID_LOG_EVENT

    (2)

    QUERY_EVENT

    header {

      version: 1

      logfileName: "20170105-162017-bin.000001"

      logfileOffset: 1920

      serverId: 1

      serverenCode: "UTF-8"

      executeTime: 1508809530000

      sourceType: MYSQL

      schemaName: ""

      tableName: ""

      eventLength: 72

    }

    entryType: TRANSACTIONBEGIN

    storeValue: " 35401"

    (3)

    TABLE_MAP_EVENT

    (4)

    WRITE_ROWS_EVENT

    header {

      version: 1

      logfileName: "20170105-162017-bin.000001"

      logfileOffset: 2040

      serverId: 1

      serverenCode: "UTF-8"

      executeTime: 1508809530000

      sourceType: MYSQL

      schemaName: "test"

      tableName: "test1"

      eventLength: 40

      eventType: INSERT

    }

    entryType: ROWDATA

    storeValue: "333012001P00b3522330020043202id 00(01000B0215Raint(11)"

    (5)

    XID_EVENT

    header {

      version: 1

      logfileName: "20170105-162017-bin.000001"

      logfileOffset: 2080

      serverId: 1

      serverenCode: "UTF-8"

      executeTime: 1508809530000

      sourceType: MYSQL

      schemaName: ""

      tableName: ""

      eventLength: 31

    }

    entryType: TRANSACTIONEND

    storeValue: "2203184"

    PS:

    如果本地没有存储position,在slave启动的时候就会调用show master status 获取master 最新的position更新到本地

    在实验中发现 master 的timeout时间设置很长, 当新日志送到canal, canal关闭,master由于收不到ack,一直hold着,再次启动canal, master hold住的事务能够进行下去,数据也落盘到master硬盘里,但再次启动的canal就没有收到之前中断掉的binlog(开始position是master最后的position)

  • 相关阅读:
    自动跳转至首页(Java Script)
    简单的轮播图(Java Script)
    蓝桥杯 2016年 第七届 四平方和(JAVA)
    蓝桥杯 2016年 第七届 剪邮票(JAVA)
    蓝桥杯 2015年 第六届 生命之树(JAVA)
    蓝桥杯 2015年 第六届 垒骰子(JAVA)
    numpy笔记
    opencv安装和运行
    vs code环境配置注意点
    numpy中matrix使用方法
  • 原文地址:https://www.cnblogs.com/devilwind/p/8039758.html
Copyright © 2011-2022 走看看