(一)Go-编写MySQL数据库驱动之Client/Server协议分析

· 1775字 · 4分钟 · 阅读量

准备:

(一)MySQL连接方式 🔗

到MySQL5.7为止,总共有五种,分别是TCP/IP,TLS/SSL,Unix Sockets,Shared Memory,Named pipes,下面我们就来看看这五种的区别:

方式 默认开启 支持系统 只支持本机 如何开启 参数配置
TCP/IP 所有系统 –skip-networking=yes/no. –port–bind-address
TLS/SSL 所有系统(基于TCP/IP)之上 –ssl=yes/no. –ssl-* options
Unix Sockets 类Unix系统 设置–socket= 来关闭. –socket=socket path
Shared Memory Windows系统 –shared-memory=on/off. –shared-memory-base-name=
Named pipes Windows系统 –enable-named-pipe=on/off. –socket=

(二)MySQL数据包格式 🔗

Server to Client数据包

Server向Client发送的数据包有两个原则:

  • 每个数据包大小不能超过2^24字节(16MB)。
  • 每个数据包前都需要加上数据包信息,也就是包头,由payload_lengthsequence_id构成。

Server to Client数据包格式:

Type Name Description
int<3> payload_length 具体的包内容长度,即payload长度
int<1> sequence_id 序列 ID 随每个数据包递增,并可能环绕。它从 0 开始,并在命令阶段中开始新命令时重置为 0。(Sequence ID)
string payload 去除包头(payload_length+sequence_id)后的数据[len=payload_length] payload of the packet

MySQL Packet

如上图红色区域蓝色为整个MySQL数据包,可以看到具体的十六进制的内容,以及它的ASCII码。

十六进制数据包:

4a0000000a352e372e3236002f000000740b50355a73387100fff7c00200ff81150000000000000000000072232a7e3709052f333f535a006d7973716c5f6e61746976655f70617373776f726400

拆分解析数据包: 整个MySQL Packet由包头+具体数据构成,采用的是小端存储形式。已知包头由payload_lengthsequence_id构成,其中payload_length占三个字节,sequence_id是占用一个字节。所以整个包头占用四个字节,包头的十六进制数据4a000000,转换为字节流[74 0 0 0]。数据内容长度74,sequence_id为0。

包头转换

Img

拆分后三部分的内容构成如下:

payload_length=4a0000

sequence_id=00

payload=0a352e372e3236002f000000740b50355a73387100fff7c00200ff81150000000000000000000072232a7e3709052f333f535a006d7973716c5f6e61746976655f70617373776f726400

Img

(三)MySQL连接交互过程 🔗

Connection Phase文档

MySQL客户端与服务器的交互主要分为两个阶段:握手认证阶段和命令执行阶段。

握手认证阶段 握手认证阶段为客户端与服务器建立连接后进行,交互过程如下:

  1. 服务器 -> 客户端:握手初始化消息
  2. 客户端 -> 服务器:登陆认证消息
  3. 服务器 -> 客户端:认证结果消息

命令执行阶段 客户端认证成功后,会进入命令执行阶段,交互过程如下:

  1. 客户端 -> 服务器:执行命令消息
  2. 服务器 -> 客户端:命令执行结果

MySQL客户端与服务器的完整交互过程如下:

连接MySQL并发送一条SHOW VARIABLES LIKE 'lower_case_%语句过程如下:

Img

  1. 第一步MySQL客户端与MySQL服务端进行TCP三次握手,建立起稳定的连接。
  2. MySQL服务端向客户端发送Server Greeting proto=10 version=5.7.26的Handshake Packet。
  3. 客户端解析的Handshake包进行逻辑处理,Login Request user=root是客户端发送的Auth Packet。
  4. 服务端认证成功客户端的Auth Packet之后,返回一个OK Packet。
  5. 可以看到我们只发送了一次请求:SHOW VARIABLES LIKE 'lower_case_%',但是客户端居然有两次Request Query,实际上第一次的Query是客户端发送SET NAMES utf8的请求到服务端,指定了客户端和服务器之间传递字符的编码规则为UTF8。第二次的Request Query是我们发送的SQL语句。
  6. 服务端接受并解析了我们的请求,把结果放在Response Packet传回来。

(四)补充字节序 🔗

MySQL的数据传输基于小端存储形式

  1. 大端序(Big-Endian)将数据的低位字节存放在内存的高位地址,高位字节存放在低位地址。这种排列方式与数据用字节表示时的书写顺序一致,符合人类的阅读习惯。
  2. 小端序(Little-Endian),将一个多位数的低位放在较小的地址处,高位放在较大的地址处,则称小端序。小端序与人类的阅读习惯相反,但更符合计算机读取内存的方式,因为CPU读取内存中的数据时,是从低地址向高地址方向进行读取的。