mysql nan_MySQL数据结构分析--NET

目的

MySQL网络通信数据结构NET,是基于Vio底层封装,用于实现Client/Server网络通信的基本处理。而核心处理主要是网络通信的读写策略,设计良好的读写策略,可以有效提高网络通信的性能。本文主要通过分析MySQL网络通信数据结构NET,进一步深入理解读写策略。

数据结构

MySQL数据结构NET,定义在源码文件/include/mysql_com.h,主要函数的实现在源码文件/sql/net_serv.cc中,主要用于实现mysql客户端与mysqld服务端进行通信。具体定义如下所示:

typedefstruct st_net {

#if!defined(CHECK_EMBEDDED_DIFFERENCES) || !defined(EMBEDDED_LIBRARY)

Vio *vio;

unsigned char *buff,*buff_end,*write_pos,*read_pos;

my_socket fd;    /* For Perl DBI/dbd */

/* The following

variable is set if we are doing several queries in one command ( as in LOAD

TABLE ... FROM MASTER ), and do not want to confuse the client with OK at the

wrong time */

unsigned long remain_in_buf,length, buf_length, where_b;

unsigned long max_packet,max_packet_size;

unsigned int pkt_nr,compress_pkt_nr;

unsigned int write_timeout, read_timeout, retry_count;

int fcntl;

unsigned int *return_status;

unsigned char reading_or_writing;

char save_char;

my_bool unused1; /* Please

remove with the next incompatible ABI change. */

my_bool unused2; /* Please

remove with the next incompatible ABI change */

my_bool compress;

my_bool unused3; /* Please

remove with the next incompatible ABI change. */

/* Pointer to

query object in query cache, do not equal NULL (0) for queries in cache that

have not stored its results yet */

#endif

/* Unused, please

remove with the next incompatible ABI change. */

unsigned char *unused;

unsigned int last_errno;

unsigned char error;

my_bool unused4; /* Please

remove with the next incompatible ABI change. */

my_bool unused5; /* Please

remove with the next incompatible ABI change. */

/** Client

library error message buffer. Actually belongs to struct MYSQL. */

char last_error[MYSQL_ERRMSG_SIZE];

/** Client

library sqlstate buffer. Set along with the error message. */

char sqlstate[SQLSTATE_LENGTH+1];

void *extension;

#ifdefined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY)

/* Controls whether a big packet should be

skipped. Initially set to FALSE by default. Unauthenticated sessions must

have this set to FALSE so that the server can't be tricked to read packets

indefinitely. */

my_bool skip_big_packet;

#endif

} NET;

从以上定义中可知,NET数据结构主要包含:网络底层封装结构Vio对象,提供底层的读写函数,具体的分析参考《MySQL数据结构分析--Vio》;用于数据操作的指针地址*buff、*buff_end、*write_pos、*read_pos,分别指向当前数据存储的首地址、结束地址、当前写位置及读位置;参数remain_in_buf、buf_length、where_b分别表示buff中剩余的数据长度、buff的总长度以及buff中数据的偏移值,length参数未使用;max_packet和max_packet_size分别表示通信包中数据的最大长度和通信包的最大长度;pkt_nr、compress_pkt_nr分别表示下一个分包以及压缩包的下一个分包;write_timeout、read_timeout、retry_count分别表示写超时时间、读超时时间以及超时重试次数;compress参数用于检测当前数据包是否为压缩方式传输,如果需要压缩,则通过zlib压缩后传输。

源码分析

对NET数据结构的处理方法进行分析,主要是网络读写策略进行分析,进一步深入了解MySQL网络通信。

基本常量

在分析具体的读写策略之前,先给出常用的一些常量:

NET_HEADER_SIZE=4:用四个字节表示NET的包头长度。

COMP_HEADER_SIZE=3:压缩包头长度。

net_data_is_ready函数

net_data_is_ready()函数用于检查socket是否读取数据已经准备好。该函数是内部函数,关键点主要是IO多路复用机制。如果有poll机制,则使用poll函数(Linux下默认都支持这种策略),poll使用pollfd数据结构,没有最大连接数限制;如果在windows环境下,使用select函数。poll和select方式随着连接数的增加,性能会影响较大,而基于事件机制的epoll,则不会存在此问题。具体的poll、select以及epoll的详细资料可参考巨著--《unix网络编程》。

net_real_write函数

net_real_write()函数是NET真正的写操作处理函数。该函数的具体处理逻辑是:首先判断是否数据是否压缩,如果需要压缩,分配内存空间并调用zlib进行压缩。然后,通过Vio通过具体的网络通信方式,进行写处理。写处理根据具体的通信方式,进行不同的写处理策略。如果写失败,在超时时间内重传。如果写成功,继续写packet中剩余的数据。具体的流程图如下所示:

以下流程图中,为了简洁直观,将压缩处理过程和写处理作为一个整体过程。而实际这两个处理过程的详细处理逻辑可参考源码,由于逻辑较简单,不再赘述。

84d5540ce00e6765c0e3028458d48363.png

图1

net_real_write()流程图

net_write_buff函数

net_write_buff()函数用于缓冲写处理过程。该函数分别对压缩和非压缩的数据进行不同的处理:首先,对于非压缩的数据,直接将传输的数据信息拷贝到net->buff缓冲中,然后调用net_real_write()函数进行网络写处理;如果数据是压缩传输,那么循环将传输的数据信息交由net_real_write()函数处理,由于压缩数据包的大小不超过MAX_PACKET_LENGTH(即压缩数据包的大小不超过16M),所以传输的数据可以在一个压缩数据包内传输结束。因为net_write_buff()处理逻辑主要是往net->buff中缓冲写处理的过程,处理逻辑较简单,不在赘述。

my_net_write函数

my_net_write()函数是对外提供网络写处理的函数。该函数主要用于封装传输的数据信息,调用底层处理逻辑进行数据包的传输。主要处理逻辑是:如果传输的数据长度大于MAX_PACKET_LENGTH(即16M),进行分包写处理;否则正常写处理。

当传输的数据长度大于16M时,每个满包的数据封装设计如下图所示。包长度为MAX_PACKET_LENGTH(16M-1),正好占用3个字节;一个字节存储下一个包的序列号,这四个字节构成了包头长度;之后空间存储包的数据信息,长度为MAX_PACKET_LENGTH。

52d5e48e1049a6586317137c8f660f01.png

图2 数据包封装设计

从以上设计可以看出,在读取包后,解析包头时,通过解析net->buff[3]包的序列号和包长度是否为MAX_PACKET_LENGTH,即可判断数据包是否乱序。

net_write_command函数

net_write_command()函数是用于传输command指令及数据信息的写处理操作。与my_net_write()函数的不同之处在于:需要多一个字节存储command指令,并且command指令放在数据net->buff[4]位置;然后在command指令后,存放数据头信息和数据信息。具体的实现细节,不再赘述。包的数据封装设计如下:

e51dc9b18c70f48ec7c85db80c1c50e6.png

图3

command数据包封装设计

my_real_read函数

my_real_read()函数是NET真正读取数据的处理逻辑。读取数据分为两个处理过程:首先读取包头信息,通过解析表头内容,处理接下来的数据读取;然后,根据包头信息,读取数据信息。在解析表头中,首先读取表头;然后检查传输是否乱序;如果是压缩传输的数据,需要读取3个字节长度数据,得到压缩数据的长度。读取数据时,根据解析的数据长度,读取数据信息。

由于读取过程共用,因此在代码设计中,使用两次循环执行。其中在第一次处理包头时,需要判断包乱序和压缩等情况。特别的,检查包乱序的方法与my_net_write()处理过程中包的封装有关,详细来说,如果当前包序列不是最后一个包,那么net->buff[0]的值为FF(即255),否则表示包发送乱序。具体处理流程如下所示:

b0436ef0988a998d6ea3e5fb2a8701e6.png

图4

my_real_read()流程图

my_net_read函数

my_net_read()函数是对外提供网络读处理的函数。该函数分别对压缩包和非压缩包进行不同处理。并且对于多个分包的情况下,将多个包的数据合并。该处理过程不对net_write_command()函数的数据格式进行读处理,仅对my_net_write()函数的正常数据封装格式进行处理。该过程中主要根据读取的数据,对net中的buff及read_pos等参数进行修改,而对压缩数据,需要调用zlib库进行解压数据处理。

结论

通过以上分析,对MySQL的网络传输结构NET及其读写处理策略有了深入的理解。在分析过程中得到以下结论:网络读写处理通过net_write_buffer()可以减少频繁的IO,提高网络吞吐率;调用底层的Vio数据结构的封装实现,进一步提高了读写的性能,具体参考《MySQL数据结构分析--Vio》;使用select/poll方式进行多路IO复用,随着连接数的增加,性能影响较大。

参考资料

1、《UNIX网络编程(卷1):套接字联网API》

2、《MySQL数据结构分析--Vio》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值