MySQL · 源码分析 · 网络通信模块浅析

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: MySQL 网络通信浅析MySQL的网络通信协议主要包含以下几个层次,从最上层的MySQL数据包协议层到最底层的socket传输:| THD| Protocol| NET| VIO| SOCKET本文主要扫一下相关的代码,以下分析基于MySQL5.7。创建会话在MySQL5.7中对会话协议层的代码进行了大量的重构以优化性能,并使得代码更加可读。以下这幅图大概展示了几个

MySQL 网络通信浅析

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

| THD
| Protocol
| NET
| VIO
| SOCKET

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

创建会话

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

mysql-connect

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

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相比之前版本的短连接性能优化:

perf-short-connection

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

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吞吐量,在这个人为构造的极端场景下,元数据的开销都非常的显著。

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
9天前
|
安全 JavaScript 前端开发
第十六届山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题—B模块安全事件响应/网络安全数据取证/应用安全
该内容描述了一次网络安全演练,包括七个部分:Linux渗透提权、内存取证、页面信息发现、数字取证调查、网络安全应急响应、Python代码分析和逆向分析。参与者需在模拟环境中收集Flag值,涉及任务如获取服务器信息、提权、解析内存片段、分析网络数据包、处理代码漏洞、解码逆向操作等。每个部分都列出了若干具体任务,要求提取或生成特定信息作为Flag提交。
12 0
|
9天前
|
安全 Linux 网络安全
第十六届山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题—A模块基础设施设置/安全加固
该任务是网络安全工程师模拟实战,目标是强化A模块服务器(Windows和Linux)的安全性。任务包括:设置强密码策略,限制用户权限,如禁用命令提示符,隐藏登录用户名;实施Nginx安全策略,禁止目录浏览,限制HTTP请求,调整超时时间,降权运行;配置日志监控,设定不同日志文件大小及覆盖规则;加固SSHD, VSFTPD, IIS服务,修改SSH端口,限制root登录,调整VSFTPD和IIS设置;优化本地安全策略,禁止匿名访问,保护密码存储,控制用户登录;最后,设计防火墙规则,限制SSH枚举,防御DoS攻击,并控制DNS解析请求。所有更改需截图并附说明,按指定格式保存提交。
10 0
|
9天前
|
安全 测试技术 网络安全
2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-B模块安全事件响应/网络安全数据取证/应用安全
该文本描述了一组网络安全挑战,涉及Windows渗透测试、数字取证和Web安全。挑战包括使用Kali Linux进行系统扫描、DNS信息提取、管理员密码获取、文件名和内容检索以及图片中单词的识别。此外,还有针对FTPServer的数据包分析,寻找恶意用户活动的痕迹如HTTP访问、端口扫描、密码和木马密码。在Web安全部分,需通过渗透测试不同页面,解决一系列逻辑谜题以获取Flag。最后,有跨站脚本渗透测试,要求访问多个服务器目录并依据页面提示触发弹框以获得Flag。
9 0
|
9天前
|
安全 测试技术 Linux
2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-A模块安全事件响应/网络安全数据取证/应用安全
该内容描述了一个网络安全挑战,涉及Windows和Linux系统的渗透测试以及隐藏信息探索和内存取证。挑战包括使用Kali Linux对Windows Server进行服务扫描、DNS信息提取、密码获取、文件名和内容查找等。对于Linux系统,任务包括收集服务器信息、提权并查找特定文件内容和密码。此外,还有对Server2007网站的多步骤渗透,寻找登录界面和页面中的隐藏FLAG。最后,需要通过FTP获取win20230306服务器的内存片段,从中提取密码、地址、主机名、挖矿程序信息和浏览器搜索关键词。
10 0
|
9天前
|
安全 测试技术 网络安全
2024年甘肃省职业院校技能大赛中职组 “网络安全”赛项竞赛样题-C模块安全事件响应/网络安全数据取证/应用安全
涉及安全事件响应和应用安全测试。需使用Kali对Windows Server2105进行渗透测试,包括服务扫描、DNS信息提取、管理员密码、文件名与内容、图片中单词等。另外,需收集win20230305的服务器端口、页面信息、脚本、登录后信息等。在Linux Server2214上,要获取SSH端口、主机名、内核版本并进行提权操作。网络安全响应针对Server2228,涉及删除后门用户、查找SSH后门时间、恢复环境变量、识别篡改文件格式和矿池钱包地址。最后,对lin20230509进行网站渗透,获取端口号、数据库服务版本、脚本创建时间、页面路径、内核版本和root目录下的flag文件内容
11 0
|
9天前
|
安全 Linux 网络安全
2024年甘肃省职业院校技能大赛中职组 “网络安全”赛项竞赛样题-C模块基础设施设置/安全加固
网络安全模块A要求对Windows和Linux服务器进行安全加固,包括密码策略(最小13字符,复杂性要求)、用户管理(禁止非管理员命令提示符,隐藏登录信息)、Nginx安全配置(限制HTTP请求,设置超时时间,降权启动)、日志监控(设置日志大小和覆盖策略)、中间件服务加固(SSH、VSFTPD、IIS服务优化)、本地安全策略(禁止匿名访问,保护密码存储,控制关机,限制Everyone权限)和防火墙策略(限制SSH连接源,防御DoS,允许特定DNS请求)。所有更改需清晰截图并附说明,保存为PDF,命名规则为“网络安全模块A-XX”并提交至U盘。
9 0
|
9天前
|
安全 测试技术 网络安全
2024年甘肃省职业院校技能大赛中职组“网络安全”赛项竞赛样题-B模块安全事件响应/网络安全数据取证/应用安全
该内容描述了一组网络安全相关的挑战,涉及Windows渗透测试、数字取证、Web安全和跨站脚本渗透。挑战包括使用Kali Linux对服务器进行扫描,提取特定服务信息、DNS、管理员密码、文件名和内容等。此外,需要分析pcapng数据包文件,找出恶意HTTP访问、扫描端口、密码和木马信息。还有Web渗透测试,需访问不同URL并解决提示以获取Flag。最后,涉及跨站脚本攻击,需在不同目录下满足条件获取弹框信息。每个部分都要求参与者找出特定的安全标志物(Flag)。
8 0
|
9天前
|
安全 Linux 网络安全
2024年甘肃省职业院校技能大赛中职组 “网络安全”赛项竞赛样题-B模块基础设施设置/安全加固
该文档是关于企业服务器系统安全加固的任务说明,包括Windows和Linux环境。主要涉及密码和登录策略强化(如复杂度、锁定机制)、用户安全管理、本地安全策略设置(如关闭清理内存、禁止未登录关闭、限制软盘访问等)、流量保护、事件监控、服务加固(SSH、VSFTPD、IIS)以及防火墙策略(DNS转发、ping限制、端口禁用、MAC过滤、IP碎片防御、SSH访问控制)。要求对各种配置进行截图并以指定格式保存提交。
8 0
|
9天前
|
SQL 安全 测试技术
2021年职业院校技能大赛“网络安全”项目 江西省比赛任务书—B模块
B模块涵盖安全事件响应和网络数据取证,涉及多项应用安全挑战。任务包括使用nmap扫描靶机、弱口令登录、生成反弹木马、权限验证、系统内核版本检查、漏洞源码利用、文件名和内容提取等。此外,还有Linux渗透测试,要求访问特定目录下的文件并提取内容。应用服务漏洞扫描涉及服务版本探测、敏感文件发现、私钥解密、权限提升等。SQL注入测试需利用Nmap扫描端口,进行SQL注入并获取敏感信息。应急响应任务包括处理木马、删除恶意用户、修复启动项和清除服务器上的木马。流量分析涉及Wireshark数据包分析,查找黑客IP、枚举测试、服务破解等。渗透测试任务涵盖系统服务扫描、数据库管理、漏洞利用模块搜索等。
13 0
|
9天前
|
监控 安全 网络安全
2021年职业院校技能大赛“网络安全”项目 江西省比赛任务书—A模块
该文档是关于企业服务器系统安全加固的任务描述,包括A模块的六个部分:登录安全、Web安全、流量保护与事件监控、防火墙策略、Windows和Linux操作系统安全配置。任务涉及设置密码和登录策略、启用安全日志、限制非法访问、调整防火墙规则、加强操作系统安全和优化服务配置等,以提升网络安全防御能力。每个部分都有具体的配置截图要求,并需按照指定格式保存提交。
12 0

相关产品

  • 云数据库 RDS MySQL 版