MySQL协议分析

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

协议头

● 数据变成在网络里传输的数据,需要额外的在头部添加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

○ 避免binaryunsafe 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_readif (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 0connect

c

c —–1—–>s 1auth

c

开始两方都为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 0query

c

c

解包的细节

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指定的行数据释放掉.

mysql 协议解析源码 c_MySQL协议分析2相关推荐

  1. Modbus通信协议+Modbus串口调试工具+Java版协议解析源码

    网络与串口二合一调试助手TCPCOM: https://download.csdn.net/download/liuyuan_java/87454762 Modbus调试工具,模拟串口调试工具 htt ...

  2. suricata smtp协议解析源码注释一

    这一篇仅对smtp协议解析的思路做整体描述,所有内容均是按照自己理解所写,肯定有理解不正确的地方,请指出,谢谢. 一.Suricata解析smtp协议整体思路 Smtp协议解析模块根据客户端与邮件服务 ...

  3. Dubbo协议模块源码剖析

    Dubbo协议模块源码剖析 目录 概 述 RPC协议报文编码与实现详解 RPC 传输实现: 拆包与粘包解决办法: Dubbo 报文格式 分析: 小结: 参考资料和推荐阅读 LD is tigger f ...

  4. Redis分布式锁解析源码分析

    Redis分布式锁解析&源码分析 概述 实战 简单的分布式锁 Redisson实现分布式锁 Redission源码分析 构造方法 获取锁lock 解锁 锁失效 红锁 案例分析 原始的写法 进化 ...

  5. mysql源码剖析–通信协议分析

    mysql源码剖析–通信协议分析 引言 1 交互过程 1.1 认证阶段 1.2 服务阶段 1.3 退出阶段 2 协议简介 2.1 server->client握手协议 2.2 client-&g ...

  6. qq音乐登录页面的html代码,musicQQ音乐协议登录源码

    [实例简介] 最近闲来无事,把一些自己的箱底代码拿出分享.... musicQQ音乐协议登录源码 纯C#代码.协议分析及代码完全由本人完成.欢迎测试. musicQQ协议 登录 源码 c# QQ音乐协 ...

  7. 【JS协议UI源码】autoJS协议源码,JavaScript界面源码,AJ脚本利用网盘就行远程关软

    autoJS协议UI源码,该源码你可以学习到JavaScript基础语法还有简单的封装md5加密 菜单判断和多号运行,本机存储和调用知识,如何不利用使用服务器进行远程关软和远程更新,远程关软我当时是利 ...

  8. openxr runtime Monado 源码解析 源码分析:CreateInstance流程(设备系统和合成器系统)Compositor comp_main client compositor

    monado系列文章索引汇总: openxr runtime Monado 源码解析 源码分析:源码编译 准备工作说明 hello_xr解读 openxr runtime Monado 源码解析 源码 ...

  9. 闲鱼代付|淘宝天猫订单监控|找人代付|淘宝代付源码/协议监控源码

    闲鱼代付|淘宝天猫订单监控|找人代付|淘宝代付源码/协议监控源码 <head><meta charset="utf-8"> <title> 淘宝 ...

  10. RedBase SQL解析源码分析

    @原创文章,转载请注明: 转载自 镜中影的技术博客 本文链接地址: RedBase SQL解析源码分析) URL:http://blog.csdn.net/linkpark1904/article/d ...

最新文章

  1. ProgreassBar 60秒走完,
  2. 32位jdk_MyEclipse 10((32/64位)、(MAC)、(Linux))软件安装教程
  3. 我来阅读lodash源码——Math(一)
  4. Intel Optane(tm) Memory Pinning 无法加载DLLiaStorAfsServiceApi.dll:找不到指定模块。(异常来自HRESULT:0x8007007E)。
  5. 这波操作,会把你的中间件架构带到另一个Level
  6. 四大价值观和12准则
  7. Linux学习之01_基础命令介绍
  8. vscode怎么安装python包_vscode如何安装python
  9. php输出楼层号,ZBlog开发中实现评论楼层号正确输出的具体方法代码
  10. 视频增强之“动态范围扩展”HDR技术漫谈
  11. 13.相机和图像——介绍,太阳摄影机,成像系统,图像形成,光圈(Aperture)_1
  12. Keras——Keras简介、安装及backend
  13. 实现主人领养宠物并带宠物去玩,狗狗叼飞碟,企鹅去南极游泳
  14. 纯数学教程 Page 324 正项级数绝对收敛的一种判别法
  15. 五一劳动节放假通知的模板(通用版5篇)
  16. MySQL Log Messages: page_cleaner: 1000ms intended loop took 8120ms.
  17. 先立业or先成家? 从收益矩阵来分析
  18. [数论][组合数学]微信群
  19. linux读内存的命令devmem,嵌入式Linux调试_命令devmem_直接读写内存
  20. jmeter调整字体大小

热门文章

  1. 代码实现UITableViewCell表视图单元定制
  2. [转]虚函数实现原理
  3. Visual Studio 2017 15.9 Previews扩展C++调试功能
  4. [Vue CLI 3] 配置 webpack-bundle-analyzer 插件
  5. sqoop数据迁移(基于Hadoop和关系数据库服务器之间传送数据)
  6. 顶级SaaS公司的共同基因都有什么?
  7. sphinx的使用1-创建索引
  8. loadrunner 字符集与检查点的探讨
  9. iptables详解001:iptables概念
  10. Linux内核入门(五)——必要的硬件知识