mysql协议重传,MySQL · 源码分析 · 网络通信模块浅析

MySQL 网络通信浅析

MySQL的网络通信协议主要包含以下几个层次,从最上层的MySQL数据包协议层到最底层的socket传输:

| THD

| Protocol

| NET

| VIO

| SOCKET

本文主要扫一下相关的代码,以下分析基于MySQL5.7。

创建会话

在MySQL5.7中对会话协议层的代码进行了大量的重构以优化性能,并使得代码更加可读。以下这幅图大概展示了几个相关的类关系(未包含诸如windows平台的相关类)

70d44825d74c7464e9c270c1afb08e18.png

创建用户线程堆栈是从主线程开始的,监听客户端请求并创建处理线程

mysqld_main

|-->connection_event_loop

|-->listen_for_connection_event

//根据不同的监听模式,去监听新请求, 当获取到一个新的监听请求时,会创建一个Channel_info类,用来存储用户的socket信息

|-->Connection_handler_manager::process_new_connection

|-->Per_thread_connection_handler::add_connection

//我们通常用的one thread one connection对应的类为Per_thread_connection_handler

|-->创建用户线程,线程函数为handle_connection

在MySQL5.7里一个重大的优化,如上所述,就是关于用户会话的thd, net, vio等信息的初始化都不是在主线程进行的,而是创建用户线程后,由用户线程自己来完成。通过这种方式,主线程可以更高效的接受新的连接请求,从而优化了在短连接场景下的性能。见官方博客 及相应的worklog

下面这幅图摘自官方博客,大家感受下5.7相比之前版本的短连接性能优化:

85569d2445a97426b8571ab2db1015bc.png

创建用户会话的主要函数栈包括:

handle_connection //线程入口函数

|-->init_new_thd

|-->Channel_info_local_socket::create_thd

|-->Channel_info::create_thd

|-->create_and_init_vio

|-->Protocol_classic::init_net

|-->my_net_init

|-->vio_fastsend //设置socket选项

* 设置IP_TOS为IPTOS_THROUGHPUT

* 设置TCP_NODELAY

|-->Global_THD_manager::add_thd

// 加入到thd链表上

|-->thd_prepare_connection

|-->login_connection

|--> check_connection

//检查链接,设置thd的链接信息,

|--> vio_keepalive // 设置SO_KEEPALIVE选项

|--> acl_authenticate // 权限认证

|-->prepare_new_connection_state

//如果连接打开了CLIENT_COMPRESS,设置NET::compress为true。

//如果设置了init_connect,则在这里执行对应的SQL语句

/* 循环接受请求并处理(do_command) */

|-->Protocol_classic::get_command

|-->Protocol_classic::read_packet

|-->my_net_read// 读取command包,这里的读超时时间由wait_timeout决定

|-->close_connection

|-->THD::disconnect

|-->THD::shutdown_active_vio

|-->vio_shutdown/* 关闭socket */

NET/VIO

my_net_write

该函数用于将数据拷贝到NET缓冲区,当长度大于MAX_PACKET_LENGTH(即4MB-1字节)会对Packet进行拆分成多个packet。每个Packet的头部都会留4个字节,其中:1~3字节,存储该packet的长度,第4个字节存储当前的packet的序号,每存储一次后递增net->pkt_nr。

每个Net对象有一个Buff(net->buff),即将发送的数据被拷贝到这个buffer中,当Buffer满时需要立刻发出到客户端。如果Buffer足够大,则只做memcpy操作。net->write_pos被更新到写入结束的位置偏移量 (net_write_buff)

如果一次写入的数据被拆分成多个Packet,那么net->pkt_nr也对应的递增多次. pkt_nr的作用是在客户端解析时,防止包发送乱序。

net_flush

实际上在my_net_write函数中,如果net->buff不够用,已经会做网络写了,net_flush最终保证所有在buff中的数据被写到网络

当客户端启用压缩协议时,这里会有些不同的,会给packet头部再加3个字节(COMP_HEADER_SIZE),被压缩的数据不包含头部的7个字节:

[3bytes:Packet的长度]

[1bytes: pkt_nr]

[3bytes:压缩后的长度]

[1bytes: compress_pkt_nr]

同样的,每个压缩包都会递增net->compress_pkt_nr

net_write_raw_loop

当packet准备好发送后,调用函数net_write_raw_loop开始进行数据发送

发送模式受vio->write_timeout影响(通过参数net_write_timeout控制);当该参数被设置成大于等于0时,使用非阻塞模式send数据包(MSG_DONTWAIT)

若网络发送被中断(EINTR),会去尝试重传

使用非阻塞模式send,每次并不保证数据全部发送完毕,因此需要循环的调用直到所有的数据都发送完毕

当输出缓冲区满时,获得错误码EWOULDBLOCK/EAGAIN,则阻塞等待(vio_socket_io_wait),最大等待时间为net_write_timeout,超时则返回错误

my_net_read

根据NET接口先读取数据包(net_read_packet):

先读取packet header,一个普通的packet header包含4个字节,压缩协议下则另外再加3个字节,如上述(net_read_packet_header)。其中的pkt_nr会提取出来和本地的值相比较。在读写两段维持的pkt_nr自增值保证了服务器和客户端的通信以一种有序的方式进行,并用于校验包的有序性。如果不一致,则说明网络包发生了乱序。直接报错。如果一致,本地net->pkt_nr++

从packet header中提取剩下的packet长度,继续从socket读取

Vio

Vio在NET的更下一层,封装了所有对socket的操作。根据不同的连接类型(TCP/IP, Socket, Name Pipe, SSL, SHARED MEMORY),相关函数指针在vio_init函数中定义,这里不展开描述

相关参数

connect_timeout: 在连接认证阶段的网络交互超时时间(ref login_connection);

wait_timeout: 等待来自客户端的新的command请求;

net_read_timeout: 一般情况下的SQL通常直接从command发过来,但拿到command后,在一条语句内可能还需要和客户端交互,这里会用到该timeout值,例如load data local infile语句;

net_write_timeout: 就是通过网络发送数据的最大超时时间;

interactive_timeout: 当客户端打开选项CLIENT_INTERACTIVE时,将当前会话的NET的wait_timeout设置为该值;

结果集

MySQL有两种常用的数据协议,一种是用于Prepared Statement,对应类为Protocol_binary,另外一种是普通的协议,对应类为Protocol_classic

我们以一个简单的表为例:

mysql> create table t1 (a int, b int);

Query OK, 0 rows affected (0.00 sec)

mysql> insert into t1 values (1,1),(2,2);

Query OK, 2 rows affected (0.00 sec)

当执行最后一条select操作时,这里使用的类为Protocol_classic

发送metadata

ref: Protocol_classic::start_result_metadata

将列的个数写入Net缓冲区

ref: Protocol_classic::send_field_metadata

逐列的准备元数据信息,包含:

| 3bytes 标识符:def

| db_name

| table_name

| org_table_name

| col_name

| org_col_name

| 字符集编码

| 列长度

| 列类型

| flags

| decimals(这里为0)

| 预留

| 预留

可以看到每个列的元数据都包含了非常多的信息,使用字符串存储,这也意味着对于一条简单的SQL,你的网络传输的内容可能大多数都是元数据,即时你的客户端可能并不需要引用到。

有多个列就写多个packet到Net buffer (Protocol_classic::end_row)

ref: Protocol_classic::end_result_metadata

write_eof_packet函数会被调用,用于标识元数据信息到此结束。此处共写5个字节(不含packet header)

发送数据

ref: end_send --> Protocol_classic::end_row

如上例,发送两行数据的packet包括

1

‘1’

1

‘1’

1

‘2’

1

‘2’

结束发送

ref: THD::send_statement_status -->net_send_eof --> write_eof_packet

发送结果结束标记,其中包含了sql执行过程中产生的warning个数

元数据开销

从上述可以看到,结果集中有很大一部分的开销是给元数据的,这意味着类似普通的pk查询,元数据的开销可能会非常昂贵。

以下贴下我之前测试过的一个例子,增加了几个选项来控制发送的元数据量:

0/METADATA_FULL: return all metadata, default value.

1/METADATA_REAL_COLUMN: only column name;

2/METADATA_FAKE_COLUMN: fake column name ,use 1,2...N instead of real column name

3/METADATA_NULL_COLUMN: use NULL to express the metadata information

4/METADATA_IGNORE: ignore metadata information, just for test..

测试表:

CREATE TABLE `test_meta_impact` (

`abcdefg1` int(11) NOT NULL AUTO_INCREMENT,

`abcdefg2` int(11) DEFAULT NULL,

`abcdefg3` int(11) DEFAULT NULL,

`abcdefg4` int(11) DEFAULT NULL,

……

……

`abcdefg40` int(11) DEFAULT NULL,

PRIMARY KEY (`abcdefg1`)

) ENGINE=InnoDB AUTO_INCREMENT=229361 DEFAULT CHARSET=utf8

使用mysqlslap测试并发pk查询

mysqlslap --no-defaults -uxx --create-schema=test -h$host -P $port --number-of-queries=1000000000 --concurrency=100 --query='SELECT * FROM test.test_meta_impact where abcdefg1 = 2'

测试结果

METADATA_FULL : 3.48w TPS, Net send 113M

METADATA_REAL_COLUMN: 7.2W TPS, Net send 111M

METADATA_FAKE_COLUMN: 9.2W TPS , Net send 116M

METADATA_NULL_COLUMN: 9.6w TPS , Net send 115M

METADATA_IGNORE: 13.8w TPS, Net send 30M

很显然无论网络流量还是TPS吞吐量,在这个人为构造的极端场景下,元数据的开销都非常的显著。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337818.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言代码优化11种实用方法

点击蓝字关注我1、选择合适的算法和数据结构选择一种合适的数据结构很重要,如果在一堆随机存放的数中使用了大量的插入和删除指令,那使用链表要快得多。数组与指针语句具有十分密切的关系,一般来说,指针比较灵活简洁,而…

spring boot注释_使用Spring Boot和注释支持配置Spring JMS应用程序

spring boot注释1.简介 在以前的文章中,我们学习了如何使用Spring JMS配置项目。 如果查看有关使用Spring JMS进行消息传递的文章介绍 ,您会注意到它是使用XML配置的。 本文将利用Spring 4.1版本中引入的改进 ,并仅使用Java config配置JMS项目…

obendclean php命令,ob_end_clean

用户评论:[#1]Sam Yong - hellclanner at live dot com [2011-05-04 22:14:35]Take note that if you change zlib output compression setting in between ob_start and ob_end_clean or ob_end_flush, you will get an error: ob_end_flush() failed to delete buffer zlib o…

C语言 | 简单工厂方法模式实现例子

点击蓝字关注我们1、简介简单工厂方法定义一个用于创建对象的类,该类接受一个参数,通过参数决定创建不同的对象。GOF并没有把简单工厂方法定义为23种设计模式之一,可以认为简单工厂方法是工厂方法的简化形式。为了体现简单工厂方法和工厂方法…

grpc 流式传输_编写下载服务器。 第一部分:始终流式传输,永远不要完全保留在内存中...

grpc 流式传输下载各种文件(文本或二进制文件)是每个企业应用程序的生死攸关的事情。 PDF文档,附件,媒体,可执行文件,CSV,超大文件等。几乎每个应用程序迟早都必须提供某种形式的下载。 下载是通…

matlab plot errorbar,如何为MATLAB errorbar plot的点和垂直线设置不同的图例?

这建立在Jens Boldsen’s answer之上,它添加了以下内容:>可以旋转表示图例中错误栏的线条使其垂直,或保持默认的水平方向;>该行的末尾用短线“关闭”.该方法非常通用,因为它支持:>任意颜色,线型和标记作为错误栏的参数.请注意,实际条形图始终绘…

C语言 / C++基础面试知识大集合

点击蓝字关注我们相对而言,C语言和C相关的面试题比较少见,没有Java方向写的人那么多,这是一篇 C 语言与 C面试知识点总结的文章,个人感觉非常难得,希望能对大家有所帮助。const作用修饰变量,说明该变量不可…

input发送a.jax_Java REST JAX-RS 2.0 –如何处理日期,时间和时间戳记数据类型

input发送a.jax无论是X-Form-Urlencoded还是JSON HTTP发布到REST资源端点,对于与日期或时间相关的数据都没有特定的“数据类型”。 大多数开发人员会将这些数据发布为“字符串”,或者只是将它们转换为Unix时间戳值(例如1435061152&#xff09…

php中is_int用法,php – is_int()和ctype_digit()之间有区别吗?

如果参数是整数类型,is_int()返回true,ctype_digit()采用字符串参数,如果字符串中的所有字符都是数字,则返回true。例:┌──────────┬───────────┬────────────────┐│ │ is_…

jooq sql_jOOQ星期二:Vlad Mihalcea深入了解SQL和Hibernate

jooq sql欢迎来到jOOQ Tuesdays系列。 在本系列文章中,我们每隔一个月的第三个星期二发布一篇文章,从jOOQ的角度采访我们发现该行业令人兴奋的人。 这包括从事SQL,Java,开放源代码以及各种其他相关主题的人员。 我们很高兴在第三…

C语言灵魂拷问:++i 为比 i++效率高?

点击蓝字关注我们相信很多人遇到过这样的问题:printf("%d,%d",i,i);也纠结过这个问题,到底答案是什么。确没有一个参考的资料。唯一知道的是,几乎所有C语言教材都这么讲:i就是先使用i的值再使i自身加一,而i则…

HibernateNONSTRICT_READ_WRITE CacheConcurrencyStrategy如何工作

介绍 在我以前的文章中 ,我介绍了READ_ONLY CacheConcurrencyStrategy ,这是不可变实体图的显而易见的选择。 当高速缓存的数据可变时,我们需要使用读写高速缓存策略,本文将介绍NONSTRICT_READ_WRITE二级高速缓存的工作方式。 内…

502无法解析服务器标头_编写下载服务器。 第三部分:标头:内容长度和范围...

502无法解析服务器标头这次,我们将探索更多HTTP请求和响应标头,以改善下载服务器的实现: Content-length和Range 。 前者表示下载量很大,后者允许部分下载文件,或者从我们开始的地方失败后继续下载。 Content-length响…

支付宝pc支付php,laravel框架下的pc支付宝支付接入

Time: 14:22*/return [//pc配置pcconfig>[partner >2088302186611, //这里是你在成功申请支付宝接口后获取到的PID;key >sxevk9h1vekjlx4y12arl6pryrz111, //这里是你在成功申请支付宝接口后获取到的Keyseller_id >208830211, //就是partnersign_type &…

最全,面中率最高的C++经典面试题分享!

点击蓝字关注我们1.new、delete、malloc、free关系delete会调用对象的析构函数,和new对应free只会释放内存,new调用构造函数。malloc与free是C/C语言的标准库函数,new/delete是C的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象…

mysql不同版本会覆盖吗,[mysql不同版本数据库同步]mysql数据库主从同步,master和slave上的mysql必须版本一样吗,如果不一样会有什么结果?...

在线QQ客服:1922638专业的SQL Server、MySQL数据库同步软件497950890Slave_SQL_Running: No mysql同步故障解决如果数据不同步可以尝试该资料mysql> show slave status\GSlave_IO_Running: YesSlave_SQL_Running: NoLast_Errno: 1062….Seconds_Behind_Master:NU…

不可变集合相比可变集合_简单的基准测试:不可变集合VS持久集合

不可变集合相比可变集合通常,您需要向集合中添加新元素。 因为您是一个优秀而谨慎的开发人员,所以您希望尽可能保持不变。 因此,向不可变集合中添加新元素将意味着您必须创建一个新的不可变集合,其中包含原始集合的所有元素以及新…

C++ 面试被问到的“左值引用和右值引用”

点击蓝字关注我们1.左值和右值在C11中可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值(将亡值或纯右值)。举个例子,int a bc, a 就是左值,其有变量名为a,通过&a可以…

php如何解释xml,PHP – 如何解析这个xml?

我正在尝试解析下面的XML,以便最终得到一个看起来像样本的数组……我很难弄清楚如何获取标签内部的属性以输出我想要的方式它…XML我想要的数组::注意添加的数组元素Array[cust] > Array[rid] > 999999[member_id] > 12345[lname] > Doe[fname] > John[address]…

jug java_架构大型企业Java项目–我的虚拟JUG会话

jug java昨天我很荣幸被邀请参加虚拟JUG 。 这是一个很大的荣誉,其原因有很多:首先,我是vJUG董事会的一员,其次,因为这是我第二次向这个对Java感兴趣的伟大团队做演讲。 受到邀请总是很高兴。 架构大型企业Java项目 过…