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

Wesley13
• 阅读 472

[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

用代码表示:

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

解码就是反过来:

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

编码长度整数

int,根据数据值大小,动态的使用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,长度固定的字符串,跟固定长度的整数一样,但区别是字符串是按数据固有顺序编码的,不需要像整数一样,从右向左编码。

Null结尾的字符串

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

变量长度字符串

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

编码长度字符串

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

包结尾字符串

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

总结

所有的数据类型如下:

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

Protocol::LengthEncodedInteger

string

Protocol::LengthEncodedString

string

Protocol::FixedLengthString

string

Protocol::VariableLengthString:

string

Protocol::RestOfPacketString

string

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

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
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
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
2年前
Android蓝牙连接汽车OBD设备
//设备连接public class BluetoothConnect implements Runnable {    private static final UUID CONNECT_UUID  UUID.fromString("0000110100001000800000805F9B34FB");
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这