MySQL协议分析2

MySQL协议分析

议程
协议头
协议类型
网络协议相关函数
NET缓冲
VIO缓冲
MySQL API

协议头

● 数据变成在网络里传输的数据,需要额外的在头部添加4 个字节的包头.
. packet length(3字节), 包体的长度
. packet number(1字节), 从0开始的递增的
● sql “select 1” 的网络协议是?

协议头

● packet length三个字节意味着MySQL packet最大16M大于16M则被分包(net_write_command, my_net_write)
● packet number分包从0开始,依次递增.每一次执行sql, packet_number清零(sql/net_serv.c:net_clear)

协议类型

● handshake
● auth
● ok|error
● resultset
    ○ header
    ○ field
    ○ eof
    ○ row
● command packet

连接时的交互

协议说明

● 协议内字段分三种形式
    ○ 固定长度(include/my_global.h)
        ■ uint*korr 解包
        ■ int*store 封包
    ○ length coded binary(sql-common/pack.c)
        ■ net_field_length 解包
        ■ net_store_length 封包
    ○ null-terminated string
● length coded binary
    ○ 避免binary unsafe string, 字符串的长度保存在字符串的前面
        ■ length<251 1 byte
        ■ length <256^2 3 byte(第一个byte是252)
        ■ length<256^3 4byte(第一个byte是253)
        ■ else 9byte(第一个byte是254)

handshake packet

● 该协议由服务端发送客户端
● 括号内为字节数,字节数为n为是null-terminated string;字节数为大写的N表示length code binary.
● salt就是scramble.分成两个部分是为了兼容4.1版本
● sql_connect.cc:check_connection
● sql_client.c:mysql_real_connect

auth packet

● 该协议是从客户端对密码使用scramble加密后发送到服务端
● 其中databasename是可选的.salt就是加密后的密码.
● sql_client.c:mysql_real_connect
● sql_connect.c:check_connection

ok packet

● ok包,命令和insert,update,delete的返回结果
● 包体首字节为0.
● insert_id, affect_rows也是一并发过来.
● src/protocol.cc:net_send_ok

error packet

● 错误的命令,非法的sql的返回包
● 包体首字节为255.
● error code就是CR_***,include/errmsg.h
● sqlstate marker是#
● sqlstate是错误状态,include/sql_state.h
● message是错误的信息
● sql/protocol.cc:net_send_error_packet

resultset packet

● 结果集的数据包,由多个packet组合而成
● 例如查询一个结构集,顺序如下:
    ○ header
    ○ field1....fieldN
    ○ eof
    ○ row1...rowN
    ○ eof
● sql/client.c:cli_read_query_result
● 下面是一个sql "select * from d"查询结果集的例子,结果 集是6行,3个字段
    ○ 公式:假设结果集有N行, M个字段.则包的个数为,header(1) + field (M) + eof(1) + row(N) + eof(1)
    ○ 所以这个例子的MySQL packet的个数是12个

resultset packet - header

● field packet number决定了接下来的field packet的个数.
● 一个返回6行记录,3个字段的查询语句

resultset packet - field

● 结果集中一个字段一个field packet.
● tables_alias是sql语句里表的别名,org_table才是表的真 实名字.
● sql/protocol.cc:Protocol::send_fields
● sql/client.c:cli_read_query_result

resultset packet - eof

● eof包是用于分割field packet和row packet.
● 包体首字节为254
● sql/protocol.cc:net_send_eof

resultset packet - row

● row packet里才是真正的数据包.一行数据一个packet.
● row里的每个字段都是length coded binary
● 字段的个数在header packet里
● sql/client.c:cli_read_rows

command packet

● 命令包,包括我们的sql语句还有一些常见的命令.
● 包体首字母表示命令的类型(include/mysql_com.h),大 部分命令都是COM_QUERY.

网络协议关键函数

● net_write_command(sql/net_serv.cc)所有的sql最终调用这个命令发送出去.
● my_net_write(sql/net_serv.cc)连接阶段的socket write操作调用这个函数.
● my_net_read读取包,会判断包大小,是否是分包
● my_real_read解析MySQL packet,第一次读取4字节,根据packet length再读取余下来的长度
● cli_safe_read客户端解包函数,包含了my_net_read

NET缓冲

● 每次socket操作都会先把数据写,读到net->buff,这是一 个缓冲区, 减少系统调用调用的次数.
● 当写入的数据和buff内的数据超过buff大小才会发出一次 write操作,然后再把要写入的buff里插入数, 写入不会 导致buff区区域扩展.(sql/net_serv.cc: net_write_buff).
● net->buff大小初始net->max_packet, 读取会导致会导致 buff的realloc最大net->max_packet_size
● 一次sql命令的结束都会调用net_flush,把buff里的数据 都写到socket里.

VIO缓冲

● 从my_read_read可以看出每次packet读取都是按需读取, 为了减少系统调用,vio层面加了一个read_buffer.
● 每次读取前先判断vio->read_buffer所需数据的长度是 否足够.如果存在则直接copy. 如果不够,则触发一次 socket read 读取2048个字(vio/viosocket.c: vio_read_buff)

MySQL API

● 数据从mysql_send_query处发送给服务端,实际调用的是 net_write_command.
● cli_read_query_result解析header packet, field packet,获 得field_count的个数
● mysql_store_result解析了row packet,并存储在result- >data里
● myql_fetch_row其实遍历result->data

________________________________

PACKET NUMBER

在做proxy的时候在这里迷糊过,翻了几遍代码才搞明白,细节如下: 客户端服务端的net->pkt_nr都从0开始.接受包时比较packet number 和net->pkt_nr是否相等,否则报packet number乱序,连接报错;相等则pkt_nr自增.发送包时把net->pkt_nr作为packet number发送,然后对net->pkt_nr进行自增保持和对端的同步.

接收包

sql/net_serv.c:my_real_read

     if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr)

发送包

sql/net_serv.c:my_net_write

   int3store(buff,len);
   buff[3]= (uchar) net->pkt_nr++;

我们来几个具体场景的packet number, net->pkt_nr的变化

连接

 c ———–> s 0  connect
 c <—-0——s 1  handshake
 c —–1—–>s 1  auth
 c <—–2——s 0  ok

开始两方都为0,服务端发送handshake packet(pkt=0)之后自增为1,然后等待对端发送过来pkt=1的包

查询

每次查询,服务客户端都会对net->pkt_nr进行清零

include/mysql_com.h
 #define net_new_transaction(net) ((net)->pkt_nr=0)
sql/sql_parse.cc:do_command
   net_new_transaction(net);
sql/client.c:cli_advanced_command

   net_clear(&mysql->net, (command != COM_QUIT));

开始两方net->pkt_nr皆为0, 命令发送后客户端端为1,服务端开始发送分包,分包的pkt_nr的依次递增,客户端的net->pkt_nr也随之增加.

 c ——0—–> s 0  query
 c <—-1——s 2  resultset
 c <—-2——s 3  resultset

解包的细节

my_net_read负责解包,首先读取4个字节,判断packet number是否等于net->pkt_nr然后再次读取packet_number长度的包体。

伪代码如下:

remain=4
for(i = 0; i < 2; i++) {
    //数据是否读完
    while (remain>0)  {
        length = read(fd, net->buff, remain)
        remain = remain - length
    }
    //第一次
    if (i=0) {
        remain = uint3korr(net->buff+net->where_b);
    }
}

网络层优化

从ppt里可以看到,一个resultset packet由多个包组成,如果每次读写包都导致系统调用那肯定是不合理,常规优化方法:写大包加预读

NET->BUFF

每个包发送到网络或者从网络读包都会先把数据包保存在net->buff里,待到net->buff满了或者一次命令结束才会通过socket发出给对端.net->buff有个初始大小(net->max_packet),会随读取数据的增多而扩展.

VIO->READ_BUFFER

每次从网络读包,并不是按包的大小读取,而是会尽量读取2048个字节,这样一个resultset包的读取不会再引起多次的系统调用了.header packet读取完毕后, 接下来的field,eof, row apcket读取仅仅需要从vio-read_buffer拷贝指定字节的数据即可.

MYSQL API说明

api和MySQL客户端都会使用sql/client.c这个文件,解包的过程都是使用sql/client.c:cli_read_query_result.

mysql_store_result来解析row packet,并把数据存储到res->data里,此时所有数据都存内存里了.

mysql_fetch_row仅仅是使用内部的游标,遍历result->data里的数据

     if (!res->data_cursor)
     {
       DBUG_PRINT("info",("end of data"));
       DBUG_RETURN(res->current_row=(MYSQL_ROW) NULL);
     }
     tmp = res->data_cursor->data;
     res->data_cursor = res->data_cursor->next;
     DBUG_RETURN(res->current_row=tmp);

mysql_free_result是把result->data指定的行数据释放掉.

时间: 2024-11-08 19:21:37

MySQL协议分析2的相关文章

mysql协议分析2---认证包

主人看到navicat和mysql在那嘻嘻哈哈,眉来眼去的,好不快乐,忽然也想自己写个程序,直接去访问Mysql,虽然现在已经有很多现成的中间件可以直接拿来用了,程序只要负责写sql语句就行了,但是主人想要自己通过mysql协议直接和mysql通讯,一窥究竟.于是主人找到Mysql说:亲爱的mysql,我以前和你交流总要通过第三方的驱动在中间传话,总感觉我们之间还有一个隔阂,有些话也不方便说,我现在有些心里话想直接和你交流...你说行吗? mysql说:当然行啊,mysql受宠若惊,要和我打交道

mysql协议分析

参考资料: http://dev.mysql.com/doc/internals/en/client-server-protocol.html http://www.haodaima.net/art/2574673 http://hutaow.com/blog/2013/11/06/mysql-protocol-analysis/#11 http://blog.csdn.net/gapaul/article/details/12046843 mysql起始握手,先由server发起,client

(3.14)mysql基础深入——mysql 日志分析工具之pt-querty-digest【待完善】

关键字:Mysql日志分析工具.mysqlsla 常用工具 [1]mysqldumpslow:官方提供的慢查询日志分析工具 [2]mysqlbinlog:二进制日志分析工具 [3]myprofi:对于只想看sql语句及执行次数的用户来说,比较推荐. [4]mysql-explain-slow-log:德国人写的一个Perl脚本,功能上有点瑕疵.不建议使用. [5]mysql-log-filter:生成简介报表日志分析,可以尝试使用一下. [6]pt-querty-digest(支持高级统计):是

蓝牙协议分析(7)_BLE连接有关的技术分析

转自:http://www.wowotech.net/bluetooth/ble_connection.html#comments 1. 前言 了解蓝牙的人都知道,在经典蓝牙中,保持连接(Connection)是一个相当消耗资源(power和带宽)的过程.特别是当没有数据传输的时候,所消耗的资源完全被浪费了.因而,对很多蓝牙设备来说(特别是功耗敏感的设备),希望在无数可传的时候,能够断开连接.但是,由于跳频(hopping)以及物理通道(Physical Channel)划分的缘故,经典蓝牙连接

BT协议分析(1)&mdash;1.0协议

简述 BT下载是采用P2P的下载方式,下载的大致形式采用如下图所示,处于图示中心的称为Tracker服务器,其余称为Peer.   缺点 1.资源的安全性 2.资源的实效性(没有上传者则BT也将失效) 3.版权 协议分析 对BT协议(1.0)的分析主要包含4个部分: 1.种子文件的分析 2.同Tracker服务器的通讯(采用HTTP协议) 3.同其他peer(配合/协同者)的通讯(采用TCP协议) 4.总结 分析前的了解 在这些分析之前,需要先了解两点BT协议采用的基础: 1.BT协议中采用的单

物联网MQTT协议分析和开源Mosquitto部署验证

在<物联网核心协议—消息推送技术演进>一文中已向读者介绍了多种消息推送技术的情况,包括HTTP单向通信.Ajax轮询.Websocket.MQTT.CoAP等,其中MQTT协议为IBM制定并力推,其具有开放.简单.轻量级以及易于实现的特点使得其即便在资源受限的环境中也能得到很好的使用,比如运行在资源紧缺型的嵌入式系统中或网络带宽非常昂贵的环境中,除此之外,它也被广泛用于遥感勘测.智能家居.能源监测和医疗应用程序等各个领域,是物联网的重要组成部分,将来可能会成为物联网的事实标准. 本篇文章将帮助

协议分析 - DHCP协议解码详解

协议分析 - DHCP协议解码详解 [DHCP协议简介] DHCP,全称是 Dynamic Host Configuration Protocol﹐中文名为动态主机配置协议,它的前身是 BOOTP,它工作在OSI的应用层,是一种帮助计算机从指定的DHCP服务器获取它们的配置信息的自举协议. DHCP使用客户端/服务器模式,请求配置信息的计算机叫做DHCP客户端,而提供信息的叫做DHCP的服务器.DHCP为客户端分配地址的方法有三种:手工配置.自动配置.动态配置. DHCP最重要的功能就是动态分配

linux 网络协议分析---3

本章节主要介绍linxu网络模型.以及常用的网络协议分析以太网协议.IP协议.TCP协议.UDP协议 一.网络模型 TCP/IP分层模型的四个协议层分别完成以下的功能: 第一层 网络接口层 网络接口层包括用于协作IP数据在已有网络介质上传输的协议.实际上TCP/IP标准并不定义与ISO数据链路层和物理层相对应的功能.相反,它定义像 地址解析协议(Address Resolution Protocol,ARP)这样的协议,提供TCP/IP协议的数据结构和实际物理硬件之间的接口. 第二层 网间层 网

网络协议分析

1. 网络模型 2. 协议分析 2.1协议架构 2. 2 以太网协议格式 2. 3 IP协议格式 2. 4 TCP协议格式 2. 5 UDP协议格式