水平有限再加上源码的复杂性,难免出现错误,请共同研究予以纠正
本文参考源码:
Net_serv.cc(主要参考)
Mysql.h.pp
Mysql_socket.h
Violite.h
Viosocket.c
Vio.c
参考书籍:
深入理解MYSQL核心技术
MYSQL核心内幕
internals-en
MYSQL官方手册
LINUX系统编程手册
注意:
1、本文将主要解析非压缩MYSQL NET包,而尽量不考虑压缩的MYSQL NET包来减小难度
2、本文主要以TCP->IP->以太网为蓝本进行描述,不考虑其他协议如(UDP)
3、本文主要以Net_serv.cc的my_net_write来描述写入socket阶段,而没有考虑net_write_command
实际上net_write_command函数是client传递命令包的主要函数入口,调用的下层函数一致
4、写入阶段可以达到net buffer满写入也可以调用net_flush()写入,但是这里无力研究net_flush()只研究满写入的情况
一、基本概念
在这之前我们必须明白如下的一些基本概念,否则不利于阅读
1、socket:是一种进程间通信的方式,可以用于多态计算机和本地两个进程进行通信,类似管道是双向
通信的一种方式,在网络上主要通过绑定IP和端口和识别唯一的网络服务端,在本地通过绑
定一个本地文件进行通信,它工作在LINUX 内核态。
2、通信协议:协议也就是客户端和服务端事先商量好的一种格式,如果格式不对则解包失败,比如TCP
协议格式如下,MYSQL有自己的通信协议。
3、MYSQL协议:MYSQL作为大型数据库系统,他有着自己的协议,至少包含如下一些数据包。
1、握手阶段
--服务端到客户端 初始化握手包
--客户端到服务端 客户端认证包
--服务端到客户端 OK包、ERROR包
2、连接建立阶段
--客户端到服务端 命令(command)包
--服务端到客户端 OK包、ERROR包、结果集包
其中结果集包包含:
1、包头包
2、FILED属性包
3、EOF包
4、行数据包
FILED属性包:为列属性每个列都会有一个
行数据包:为返回数据每行一个包
如果一个SELECT 返回 2行3列数据
会包含3(列)+2(行)+1(包头包)+2(EOF包)个数据包
由于MYSQL数据包的复杂性本文并不准备解析MYSQL协议各种包,可以参考:
MYSQL核心内幕
internals-en
下图是展示了MYSQL 服务端和客户端之间如何握手成功,并且进行数据传输
我们约定它叫做MYSQL数据包
4、MYSQL NET包:它是实际的传输包的大小,大小最大为16M-1,这是在源码中定义死了的,每个MYSQL NET包
包含一个包头,至少包含4个字节(非压缩包,如果压缩包会多3个字节),如下:
3 bytes:(压缩后)payload长度
1 bytes:序号
(压缩)3 bytes:压缩前payload长度
其中payload就是实际数据
比如这样一个MYSQL NET包:
为什么有一个序号呢?因为为了保证每个命令发送的包是有序的,比如一个结果
集合包会包含多个包,而其中的行数据包(SERVER->CLIENT的时候每一行数据是一个MYSQL数据包)
包很可能大于16M-1,那么我们就需要将整个结果集包分为多个MYSQL NET包进行传输,当到达
client的时候保证他顺序。当然并不是每个MYSQL NET包都很大,比如一些MYSQL数据包如OK包,就很
小,我们知道在以太网传输的最大帧为MTU 1500字节,那么可能出现一个以太网帧包含多个MYSQL NET
包(如OK包),也可能一个MYSQL NET包在多个以太网帧中,同时可能出现一个MYSQL数据包在多个MYSQL
NET包中,但是最小一个MYSQL NET包至少包含一个MYSQL数据包(如OK包),当然TCP
注意当一个MYSQL数据包分为多个MYSQL NET包的时候其最后会紧跟一个长度为0作为结束的标识,源码中
if (!pkt_len)
goto end;
我们约定它叫做MYSQL NET包
5、NET结构体说明
下面先来看几个截图说明:
可以看到NET结构中封装了一个BUFFER,而这个BUFFER正是由参数net-buffer-length控制
其大小不能超过参数max-allowed-packet大小的这个buffer,本文约定将它叫做net buffer。
net-buffer-length 默认16K最大为1M
max-allowed-packet 默认4M最大1G
结构体还封装了2个unsigned int的变量write_timeout,read_timeout. 他们正是
net-wirte-timeout,net-read-timeout参数指定,用来表示在返回一个ETIMEDOUT错误前能够
KEEPLIVE最大的时间。
设置超时的底层调用很有可能是
ret= mysql_socket_setsockopt(vio->mysql_socket, SOL_SOCKET, optname,optval, sizeof(timeout));
之类的调用
另外结构体还封装了retry_count这是在遇到EINTR错误的时候重试的次数由参数net-retry-count
控制,在后面将会讲述
6、LINUX ETIMEDOUT、EINTR、EWOULDBLOCK、EAGAIN
#define ETIMEDOUT 110 /* Connection timed out */
#define EINTR 4 /* Interrupted system call */
#define EAGAIN 11 /* Try again */
#define EWOULDBLOCK EAGAIN /* Operation would block *
7、LINUX平台下MYSQL读取和写入scoket函数
位于Mysql_socket.h中
send(mysql_socket.fd, buf, IF_WIN((int),) n, flags);
recv(mysql_socket.fd, buf, IF_WIN((int),) n, flags);
当然如果是WIN_32平台send和recv函数有底层封装
8、包封装流程
如下图:
本文研究是应用层MYSQL通过自己的协议进行数据包封装后如何进行传输的
二、MYSQL数据包的写入scoket阶段
1、将可能大的MYSQL数据包进行拆分
函数原型
my_bool my_net_write(NET *net, const uchar *packet, size_t len)
net:NET结构体指针
packet:MYSQL数据包指针,MYSQL数据包由MYSQL协议栈准备好
len:MYSQL数据包长度
这个过程会将大的MYSQL数据包进行拆分打包为多个MYSQL NET包,如果是小的MYSQL数据包(如OK包)就进行
打包为MYSQL NET包调用net_write_buff下面我将我写的中文注释加上源码部分一同放出如下:
点击(此处)折叠或打开
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数265
191.63 KB下载数245
143.91 KB下载数1148
183.71 KB下载数642
644.84 KB下载数2756
相关文章
发现更多好内容猜你喜欢
AI推送时光机 咦!没有更多了?去看看其它编程学习网 内容吧