MySQL协议分析2
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的更多相关文章
- MySQL协议分析
MySQL协议分析 标签: mysql 2015-02-27 10:22 1807人阅读 评论(1) 收藏 举报 分类: 数据库(19) 目录(?)[+] 1 交互过程 MySQL客户端与 ...
- MySQL协议分析(2)
MySQL协议分析(2) 此阶段是在压缩传输无加密条件下进行的协议分析 思路 结合Oracle官网的说明和自己用wireshark加python进行数据包分析 步骤 客户端与服务器端是否压缩的协商阶段 ...
- MySQL协议分析(1)
MySQL协议分析 此阶段的协议分析是在未压缩未加密情况下的协议分析 思路: 结合Oracle官网和自己用wireshark抓的网络数据包进行协议分析 官网说明 mysql包共分为4段,格式如下: 第 ...
- mixer: mysql协议分析
综述 要实现一个mysql proxy,首先需要做的就是理解并实现mysql通讯协议.这样才能通过proxy架起client到server之间的桥梁. mixer的mysql协议实现主要参考mysql ...
- mysql 协议分析
MYSQL Binlog协议分析 此处不讨论建立连接,验证和handshake的交互协议 Binlog协议 一个MYSQL 通信包由包头包体组成 包体根据具体的交互协议有自身的组成结构, 在binlo ...
- mysql协议分析2---认证包
主人看到navicat和mysql在那嘻嘻哈哈,眉来眼去的,好不快乐,忽然也想自己写个程序,直接去访问Mysql,虽然现在已经有很多现成的中间件可以直接拿来用了,程序只要负责写sql语句就行了,但是主 ...
- mysql协议分析1---报文的格式和基本类型
navicat 和 mysql 是一对好基友,每天都有非常频繁的交流,主人在navicat上写下每条sql语句,轻轻的点了下执行按钮,navicat就飞快的把主人的指令传送到mysql那里,mysql ...
- mysql协议简析
前言 如果要在命令行中连接mysql,最常用的便是 mysql -u root -p 这样指定用户名和密码 当然还可以使用远程连接 mysql -h 127.0.0.1 -u root -p 还有一种 ...
- Memcache的使用和协议分析详解
Memcache的使用和协议分析详解 作者:heiyeluren博客:http://blog.csdn.NET/heiyeshuwu时间:2006-11-12关键字:PHP Memcache Linu ...
随机推荐
- 怎样将MySQL数据库上传到服务器
首先,需要将本地的数据库导出来,作为一个数据文件,以备稍后上传到服务器用,在本地登陆phpmyadmin控制面板: 登陆成功后,在左侧选择需要操作的数据库: 选择后,页面会自动刷新,然后再在右边点击[ ...
- linux的学习系列 3---目录
目录也是一个文件,它的唯一功能是用来保存文件及其相关信息.所有的文件,包括普通文件.设备文件和目录文件,都会被保存到目录中. 主目录 登录后,你所在的位置就是你的主目录(或登录目录),接下来你主要是在 ...
- POJ 3468<线段树,区间add>
题目连接 //位运算 k<<1 相当于 k*2 k<<1|1 相当于 k*2+1 /* 修改区间内的值,并且维护区间和. 详见代码 */ #include<cstdio& ...
- php ajax 下拉加载数据
视图 <html> <head> <title>健康知识</title> <script type="text/javascript&q ...
- js框架——angular.js(3)
1. 过滤filter 过滤就是将内容进行筛选或者转换或者两者都有,一般的表示方式就是在变量后面添加"|",然后加上过滤条件,如—— {{name|currency}} 这个cur ...
- Linux学习 -- Shell基础 -- Bash基本功能
历史命令 history -c clear -w 写入 ~/.bash_history 默认保存1000条, 可在/etc/profile中修改 调用 Tab补全 命令.目录.文件 命令别名 ...
- Java学习之位运算符
位运算符:&,|,^,~,<<,>> & (按位与):只有对应的两个二进制位均为1时,结果才为1.例如,9&5,即00001001&000001 ...
- sites
search http://213.240.44.13/ http://1.179.252.95/
- Reverse complement DNA
用法:python rev_comp.py input.fa out.fa 输入文件为 fasta 格式文件,若输入文件中序列的 header 有 '+' 或 '-' 号标记正负链,则带有 '+' 的 ...
- 代码异味---Code smell
程序员应该竭尽全力去写那些重复的代码.以下几点是我目前最需要改进的地方. 重复代码: 相同或者相似的代码存在于一个以上的地方. 长方法: 一个非常长的方法.函数或者过程. 巨类: 一个非常庞大的类. ...