MySQL协议分析

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

协议头

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

协议头

● 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< byte
■ length <^ byte(第一个byte是252)
■ length<^ 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() + field (M) + eof() + row(N) + eof()
○ 所以这个例子的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 + ] != (uchar) net->pkt_nr)

发送包

sql/net_serv.c:my_net_write

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

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

连接

 c ———–> s   connect
c <—-——s handshake
c —–—–>s auth
c <—–——s 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 ———–> s   query
c <—-——s resultset
c <—-——s resultset

解包的细节

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

伪代码如下:

remain=
for(i = ; i < ; i++) {
//数据是否读完
while (remain>) {
length = read(fd, net->buff, remain)
remain = remain - length
}
//第一次
if (i=) {
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指定的行数据释放掉.

MySQL协议分析2的更多相关文章

  1. MySQL协议分析

    MySQL协议分析 标签: mysql 2015-02-27 10:22 1807人阅读 评论(1) 收藏 举报  分类: 数据库(19)    目录(?)[+]   1 交互过程 MySQL客户端与 ...

  2. MySQL协议分析(2)

    MySQL协议分析(2) 此阶段是在压缩传输无加密条件下进行的协议分析 思路 结合Oracle官网的说明和自己用wireshark加python进行数据包分析 步骤 客户端与服务器端是否压缩的协商阶段 ...

  3. MySQL协议分析(1)

    MySQL协议分析 此阶段的协议分析是在未压缩未加密情况下的协议分析 思路: 结合Oracle官网和自己用wireshark抓的网络数据包进行协议分析 官网说明 mysql包共分为4段,格式如下: 第 ...

  4. mixer: mysql协议分析

    综述 要实现一个mysql proxy,首先需要做的就是理解并实现mysql通讯协议.这样才能通过proxy架起client到server之间的桥梁. mixer的mysql协议实现主要参考mysql ...

  5. mysql 协议分析

    MYSQL Binlog协议分析 此处不讨论建立连接,验证和handshake的交互协议 Binlog协议 一个MYSQL 通信包由包头包体组成 包体根据具体的交互协议有自身的组成结构, 在binlo ...

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

    主人看到navicat和mysql在那嘻嘻哈哈,眉来眼去的,好不快乐,忽然也想自己写个程序,直接去访问Mysql,虽然现在已经有很多现成的中间件可以直接拿来用了,程序只要负责写sql语句就行了,但是主 ...

  7. mysql协议分析1---报文的格式和基本类型

    navicat 和 mysql 是一对好基友,每天都有非常频繁的交流,主人在navicat上写下每条sql语句,轻轻的点了下执行按钮,navicat就飞快的把主人的指令传送到mysql那里,mysql ...

  8. mysql协议简析

    前言 如果要在命令行中连接mysql,最常用的便是 mysql -u root -p 这样指定用户名和密码 当然还可以使用远程连接 mysql -h 127.0.0.1 -u root -p 还有一种 ...

  9. Memcache的使用和协议分析详解

    Memcache的使用和协议分析详解 作者:heiyeluren博客:http://blog.csdn.NET/heiyeshuwu时间:2006-11-12关键字:PHP Memcache Linu ...

随机推荐

  1. angular初始用——简易购物车

    <html> <head> <meta charset="utf-8"> <script src="js/angular.js& ...

  2. Hadoop: LongWritable cannot be cast to org.apache.hadoop.io.IntWritable

    写MR Job的时候遇到一个坑爹的异常: LongWritable cannot be cast to org.apache.hadoop.io.IntWritable 当写Map的时候,key的默认 ...

  3. poj 1654 Area(计算几何--叉积求多边形面积)

    一个简单的用叉积求任意多边形面积的题,并不难,但我却错了很多次,double的数据应该是要转化为long long,我转成了int...这里为了节省内存尽量不开数组,直接计算,我MLE了一发...,最 ...

  4. plat模板修改记录

    每个栏目显示文章数 homepage.hph 94行 $loop = new WP_Query('post_type=post&showposts=6&category_name=xi ...

  5. 3398: [Usaco2009 Feb]Bullcow 牡牛和牝牛

    3398: [Usaco2009 Feb]Bullcow 牡牛和牝牛 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 243  Solved: 167[S ...

  6. jq之简单的表单验证

    <body> <form method="post" action=""> <div class="int"& ...

  7. 保存iptables的防火墙规则的方法【转载】

    转自: 保存iptables的防火墙规则的方法 - 51CTO.COMhttp://os.51cto.com/art/201103/249504.htm 保存iptables的防火墙规则的方法如下: ...

  8. HDU 1934 树状数组 也可以用线段树

    http://acm.hdu.edu.cn/showproblem.php?pid=1394 或者是我自己挂的专题http://acm.hust.edu.cn/vjudge/contest/view. ...

  9. linux性能优化cpu 磁盘IO MEM

    系统优化是一项复杂.繁琐.长期的工作,优化前需要监测.采集.测试.评估,优化后也需要测试.采集.评估.监测,而且是一个长期和持续的过程,不 是说现在优化了,测试了,以后就可以一劳永逸了,也不是说书本上 ...

  10. 2304: Lights Out(枚举)

    枚举第一行所有可能的的情况 #include<iostream> #include<cstdio> #include<cstring> #include<al ...