MySQL通讯协议(1)数据类型

[TOC]

MySQL通讯协议(1)数据类型

对于大部分开发者来说,并不需要了解MySQL客户端和服务端是如何交互的。但是当业务发展到一定阶段,数据量增大时,分库分表就成了不得不考虑的一种优化措施。目前主流的方案主要有两种,一种是本地代理连接、分析重写SQL、路由、执行、合并结果,一种是把这些放到中间件里。而后一种就必须了解MySQL通讯协议,因为这个中间件要作为客户端与MySQL服务交互,还要作为服务端与MySQL客户端交互。

连接MySQL的协议

协议

支持的操作系统

TCP

全部

使用TCP/IP协议连接本地或远程服务器

SOCKET

Unix

使用Unix socket连接本地服务器

PIPE

Windows

使用Windows named-pipe连接本地服务器

MEMORY

Windows

使用Windows shared-memory连接本地服务器

可见后三种方式都受制于各自平台,且只能连接本地服务器,不符合大部分应用场景。所以,我们接触到的都是基于TCP协议的连接方式。

MySQL通讯协议是基于TCP/IP协议的一个应用层协议,所以TCP/IP协议该有的三次握手、滑动窗口、重传机制都有,这里不再赘述。

概述

MySQL通讯协议被设计用于MySQL客户端和MySQL服务端之间通讯,主要用于:

  • 各种语言实现的连接器,如JDBC驱动
  • MySQL代理
  • 主从复制之间的通信

基本数据类型

协议里用到的数据类型,有Integer和String两种

Integer

整数类型,分为两种固定长度和

固定长度整数

占用空间固定的无符号整数,有6种int<1>、int<2>、int<3>、int<4>、int<6>、int<8>,尖括号里的数字,代表占用几个字节。可以简单理解成byte、short、int、long,但是实际表示并不一样。因为网络传输的单位是字节,即8个bit,int<2>以上没法直接传输,必须经过简单的编码。

编码方式就是把要编码的值,从右向左,每次拿一个字节,依次排列。

例如一个int<2>类型的值2020,转成二进制:111 11100100,第一个字节:11100100,第二个字节:00000111,所以编码后的数据就是:11100100 00000111 ,用十六进制表示:0xE4 0x07

用代码表示:

1int a = 2020; 2byte[] data = new byte[2]; 3data[0] = (byte)(a & 0xFF) 4data[1] = (byte)(a & >>> 8)

解码就是反过来:

1int[] data = new int[]{228, 7}; 2int i = (data[0] & 0xff) | ((data[1] & 0xff) << 8);

编码长度整数

int<lenenc>,根据数据值大小,动态的使用1、3、 4、9个字节保存。类似于UTF-8的编码方式。

  • 如果值 < 251,就用1字节表示。
  • 如果值 ≥ 251 且 < (2^16),就用0xfc + 2字节表示,共3字节。
  • 如果值 ≥ (2^16) 且 < (2^24),就用0xfd + 3字节表示,共4字节。
  • 如果值 ≥ (2^24)且 < (2^64),就用0xfe + 8字节表示,共9字节。

例如,值300,属于第二种情况,转成2字节为:00101100 00000001,前面加上0xfd,最后结果就是:0xfd 0x2C 0x01。

解码就是先取第一位,判断是否<251,如果如就是第一种情况,如果不是,根据第一位是0xfc0xfd0xfe 判断是那种情况,然后读取后面N位即可。

这种编码方式的好处是,在不能提前确定数据大小时,提供一种动态的编码方式,当实际数值较小时,可以不用传输大量的空值(占位的0),从而减少数据包大小,提高传输效率。

String

字节序列类型,分为5种类型

固定长度字符串

string<fix>,长度固定的字符串,跟固定长度的整数一样,但区别是字符串是按数据固有顺序编码的,不需要像整数一样,从右向左编码。

Null结尾的字符串

string<NUL>,长度不固定,但是已一个空数据(0x00)结尾的字符串。类似于HTTP协议Head的结束标志\r\n\r\n,只要读到0x00就认为结束。

变量长度字符串

string<var>,字符串的长度由另一个字段决定,或者在运行时计算。比如说消息体的长度,就是由消息头里的_payload_length_这个字段指定的。

编码长度字符串

string<lenenc>,用前缀整数指定长度的字符串,跟编码长度整数字符串一致。

包结尾字符串

string<EOF>,放在包最后的字符串,因为在最后,所以只要用包长度 - 已读长度就是剩余字符串的长度了。

总结

所有的数据类型如下:

Type

Description

int<1>

1 byte Protocol::FixedLengthInteger

int<2>

2 byte Protocol::FixedLengthInteger

int<3>

3 byte Protocol::FixedLengthInteger

int<4>

4 byte Protocol::FixedLengthInteger

int<6>

6 byte Protocol::FixedLengthInteger

int<8>

8 byte Protocol::FixedLengthInteger

int<lenenc>

Protocol::LengthEncodedInteger

string<lenenc>

Protocol::LengthEncodedString

string<fix>

Protocol::FixedLengthString

string<var>

Protocol::VariableLengthString:

string<EOF>

Protocol::RestOfPacketString

string<NUL>

Protocol::NulTerminatedString

那么为什么要定义这些数据类型?因为在网络传输数据时,有两个问题:1,数据表示什么,2,数据边界在哪。

例如:客户端向服务器提交用户名密码,数据在网络上是一个个字节发送的,服务器怎么知道哪段是用户名,哪段是密码。最常见的解决办法是这样的:

{"username":"san", "password":"123"}

纯文本、便于阅读、结构清晰、没有特殊关键字、没有过多无用符号,比xml好多了。但是仍然不够,大括号、双引号、冒号,都是额外的开销,当数据量大到一定地步,都是巨大的资源浪费。这就是很多大厂开发RPC框架的原因,例如Dubbo、Protobuf(当然对于服务调用HTTP本身也很浪费资源)。

MySQL这种对性能有极致要求的数据库当然也不能这么做,同时,因为数据库通讯数据结构很固定,每次传输的字段基本没变化。所以可以这么做:

  • 按提前规定好的顺序发送每个字段值,比如先发用户名,后发密码

  • 特殊的标志位、常量直接规定长度,比如成功/失败,一个字节就够了

  • 不确定的值就用各种方法描述,动态长度字符串

这样就可以把没用的数据去掉了,对于一个登陆请求,客户端完全可以传:3san3123。实际上Protobuf之所以快,就是用类似的方法做的。

参考资料:

https://dev.mysql.com/doc/refman/8.0/en/connection-options.html#option_general_protocol

https://dev.mysql.com/doc/dev/mysql-server/8.0.19/page_protocol_basic_data_types.html

https://dev.mysql.com/doc/internals/en/basic-types.html

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

KVM调整cpu和内存

一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid

Twitter的分布式自增ID算法snowflake (Java版)

概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移