MySQL 协议简介
MySQL 数据库本身是一个很典型的 C/S 结构的服务,客户端和服务端通信可以通过 Tcp 和 Unix Socket 的方式进行交互。在本篇中,我们主要说明通过网络 Tcp 的方式来实现 MySQL 代理。
MySQL 客户端和服务端的交互主要包含了两个重要的过程:1. 握手认证,2.发送命令。本篇会主要围绕这两个过程来介绍相关的实现。在客户端和服务端交互的过程中主要包含了以下几种类型报文:数据包、数据结束包、成功报告包以及错误消息包,在后面的章节中会为大家详细介绍这几种报文。
交互过程
MySQL 客户端和服务端在交互的过程中主要包含了两个过程,即握手认证阶段和执行命令阶段,当然在这两个过程之前首先要经历 TCP 三次握手的过程。在三次握手结束后首先进入握手认证阶段,在交换完信息并且客户端正确登录服务端后,进入执行命令阶段,图 3 完整描述了整个交互过程。
握手认证
在握手认证阶段是 MySQL 客户端和服务端建联非常重要的阶段,该阶段发生在 TCP 三次握手之后。首先服务端会给客户端发送服务端信息,其中包括协议版本号、服务版本信息、挑战随机数、权能标志位等等。当客户端接收到服务端发来的响应之后,客户端开始发起认证请求。认证请求中,会携带客户端用户名、数据库名以及通过服务端响应中的挑战随机数将客户端密码加密后,一同发送给服务端进行校验。校验过程中,除了会对用户名密码进行校验,还会匹配客户端所使用的认证插件,如果不匹配则会发生插件的自动切换,以及判断客户端是否使用了加密链接。当以上阶段全部正常完成后,客户端则登录成功,服务端返回客户端 OK 数据报文。
上述过程分别在 runtime 和 protocol 的 server 中实现。在 runtime 中的 start 函数等待请求进入,Tcp 三次握手结束后,从 handshake 函数如图 3,开始握手阶段。在 handshake 中分别包含了三个过程,首先由 write_initial_handshake 给客户端发送服务端信息,然后在 read_handshake_response 客户端拿着服务端信息开始认证请求。
执行命令
当握手和认证阶段完成后,此时客户端才算是真正意义上的与服务端完成建立链接。那么此时则进入执行命令阶段。在 MySQL 中,能够发送命令的指令类型有很多种,我们会在下文中为大家进行介绍。
代码链接: github.com/database-me….
在下面的代码中可以看到,Pisa-Proxy 在这里会对不同类型的指令进行不同的逻辑处理。例如,初始化 db 的处理逻辑为 handle_init_db,处理查询的逻辑为 handle_query。
MySQL 协议基本数据类型
在 MySQL 协议中主要有以下几种数据类型:
整型值
MySQL 报文中整型值分别有 1、2、3、4、8 字节长度,使用小端传输。
字符串(以 NULL 结尾)(Null-Terminated String)
字符串长度不固定,当遇到'NULL'(0x00)字符时结束。
二进制数据(长度编码)(Length Coded Binary)
参考链接:dev.mysql.com/doc/interna…
报文结构
在 MySQL 客户端和服务端所交互的数据最大长度为 16MByte,其基本数据报文结构类型如下:
在图 4 和图 5 中描述了 MySQL 报文的基本结构。报文包括了两部分,消息头和消息体。其中在消息头中,3 字节表示数据报文长度,1 字节存储序列号,消息体中存储实际的报文数据。
消息头
用于标记当前请求消息的实际数据长度值,以字节为单位,占用 3 个字节,最大值为 0xFFFFFF,即接 2^24-1。
序列号
序列 ID 从 0 开始随每个数据包递增,并且当进入新的命令时重置为 0。序列号 ID 有可能会发生回绕,当发生回绕时,需将序列号 ID 重置为 0,并且重新开始计数递增。
报文数据
消息体用于存放请求的内容及响应的数据,长度由消息头中的长度值决定。
客户端请求命令报文
该指令用于标识客户所要执行命令的类型,以字节为单位占用 1 个字节。请求命令报文常用到的有 Text 文本协议和 Binary 二进制协议。这里可以参考【执行命令】中的代码,代码中描述了对不同指令的处理逻辑。
在文本协议中常用到的有以下指令:
值
指令
功能
0x01
COM_QUIT
关闭连接
0x02
COM_INIT_DB
切换数据库
0x03
COM_QUERY
SQL 查询请求
0x04
COM_FIELD_LIST
获取数据表字段信息
0x05
COM_CREATE_DB
创建数据库
0x06
COM_DROP_DB
删除数据库
0x08
COM_SHUTDOWN
停止服务器
0x0A
COM_PROCESS_INFO
获取当前连接的列表
0x0B
COM_CONNECT
(内部线程状态)
0x0E
COM_PING
测试连通性
更多请参考:dev.mysql.com/doc/interna…
在二进制协议,即 Prepare Statement 中常用到的有以下指令:
更多请参考:dev.mysql.com/doc/interna…
响应报文
OK 响应报文
Error 响应报文
Field 结构
EOF 结构
Row Data 结构
......
例如,下面代码中展示了如何给客户端写 OK 报文。