MySQL是怎样运行的读书笔记1

今天读了《MySQL是怎样运行的》前四章。1、2章感觉挺无聊,说了一些常识性的概念。第三章讲述了字符集和比较规则,开始比较有用。

MySQL字符集及比较规则

字符集

字符集很好理解,我们常见的UTF8、GB2312、GBK 等等都是常见字符集,字符集比较难以处理的问题在于,不同字符集造成字符存储的字节数不一致。

ASCII字符集最为简朴,128个字符,一个字节编码。
GB2312\GBK如果该字符在ASCII字符集中,则于ASCII一致,否则采用两个字节。即会出现1-2个字节用来存储一个字符
UTF-8变长编码方式采用1-4个字节存储一个字符。

老生常谈,MySQL中utf8字符集是utf8mb3字符集,来自正经UTF-8的阉割版本,采用1-3个字节存储,正经UTF-8得使用utf8mb4字符集,采用1-4个字符集进行存储。

比较规则

通常我们建库/建表时都要选择字符集,MySQL的选择都会有好大串,让人迷惑,就算你选了utf8mb4依旧会出现很多后缀的选项。这些后缀其实就是比较规则。
MySQL的比较规则名称是这样的:

编码_应用的语言_是否区分重音/大小写

后缀 英文释义 描述
_ai accent insensitive 不区分重音
_as accent sensitive 区分重音
_ci case insensitive 不区分大小写
_cs case sensitive 区分大小写
_bin binary 以二进制方式比较

比较集的采用配置有四种级别:
列级别、表级别、库级别、服务器级别

客户端和服务器通信中使用的字符集

建立连接后,会产生一个Session级别的变量 character_set_client 维护使用的字符集,该变量描述客户端请求的字符串编码字符集;character_set_connection变量描述客户端接到的字符集转为什么字符集给到服务器处理;character_set_result描述服务端将结果转换为什么字符集给到客户端。

InnoDB记录存储解构

InnoDB页简介

innoDB将数据分为若干页读写于磁盘,默认中页大小为16KB,每次读写底层都是按16KB为单位进行读写,页大小可以变更,但是只能在初始化MySQL数据目录时指定,之后不可改。

InnoDB行格式

目前InnoDB支持4种行格式:COMPACTREDUNDANTDYNAMICCOMPRESSED

COMPACT行格式

|–变长字段长度列表–|--NULL值列表–|--记录头信息–|--列1的值–|--列2的值–|--…–|--列n的值–|

1、变长字段长度列表,用以记录如VARCHAR(M)、TEXT等数据长度不一定的字段的长度信息,即一条变长数据,需要有两个内容存放于行中:
数据内容、该数据占用的字节数。倒序存储了每个变长字段的内容长度。

但是变长字段长度记录是个问题,如VARCHAR(M)这样能确定最大字符数的,
引入三个变量:

W:表示采用字符集的最大单字符长度,如ASCII为1,utf8mb4为4

M:即最大字符数

L:即真实数据长度(真实编码后,每个字符不定长)

如果M x W <= 255 则用一个字节标识数据占用的字节数

如果M x W > 255,两种情况:

如果L<=127(2^8 - 1)则用1字节标识占用的字节数,如果L>127则2字节表示占用的字节数,使用的时候如何确定到底是1个字节还是2个字节呢?看字节第一位是否为0,单字节数字不大于127时,第一位总为0,如果第一位为1,则该字节是半个字段长度。

如果长度很长,超过2字节所能记录的长度,采用了溢出页方案,2字节长度足够标识留在本页种的字节长度。

另外,变长字段长度列表只记录非NULL列的内容长度。如果表中所有列都不是变长数据类型或者所有值都是NULL,就不需要这个列表

2、NULL值列表

如果表中不允许有可NULL的咧,NULL值列表不存在(让我想起很多MySQL大佬反对使用可空列),NULL值列表使用二进制按位标识的方式来维护NULL值,NULL值所在的位置标识为1时标识该列为NULL,

3、记录头信息

头部有5个字节(40个二进制位)用以描述部分信息及索引信息

4、记录的真实数据

除了传入的数据外,还会增加隐藏列,默认情况下,会有row_id(无唯一标识列会加,否则不加)、trx_id,roll_pointer

5、定长字段处理方式CHARM(M)

在COMPACT中,如果采用定长字符集,如ASCII,则不加到变长字段长度列表中,如果采用变长字符集,如utf8mb4,则会加到变长字段长度列表中。但是采用了定长字段,就算存空字符串,最少得占M个字节,用变长字段则没有这个限制。

REDUNDANT行格式

老格式

1、字段长度偏移列表。

但是把所有列的长度都逆序存储到字段长度偏移列表。不管变不变长。其实存的就是记录结束所在字节处。没有直接记录字段长度,要知道字节长度进行计算就行。

2、记录头信息

头信息中,有一个1byte_offs_flag字段,标记字段长度偏移列表中每个列对应的偏移量是使用1字节还是2字节表示。REDUNDANT行格式会根据记录真实占用大小进行判断赋值。还是那个问题,如果占用不大于127则置为0,否则为1,如果更大则跑到了溢出页中,2个字节就够。

3、NULL值处理

REDUNDANT中没有NULL值列表,在对应偏移量的第一位作为是否为NULL的依据。1为NULL,否则非NULL。如果NULL值所在字段是定长字段,类似CHAR(M),即时是NULL,也将占用M字节长度,用0填充。如果是变长类型,则不占任何空间,通过不同列相同偏移量可表示出来。

4、CHAR(M)列的存储格式

使用最大的字符集长度M来分配,如GBK,则始终为2M长度。

溢出列

在COMPACT、REDUNDANT行格式中,某一列数据特别多,本记录真实数据只会存储该列前768字节数据及一个致癌性那个其他页的地址,把剩下的数据存放到其他页中,786字节之外数据称为溢出页;我们把需要溢出页来存储的列称为溢出列。

一个列存储多少字节数据后会变成溢出列呢?
限制条件如下:

MySQL规定一个页中至少存放两条记录;每个页会有132字节的额外信息;

假设某张表,只有一个字段。

每个记录需要的额外信息是27字节

2字节用于存储真实数据长度

1字节存储是否是NULL值

5字节头信息

6字节ROW_ID

6字节TRX_ID

7字节ROLL_POINTER

假设一个列真实数据占用字节为n,如果要不发生溢出,则需要满足:

132+2*(27+n)<16384

即n<8099。但是这个只是单字段表的数值。

在DYNAMIC和COMPRESSED行格式的行格式中,真实数据处不存储768字节的数据,而把所有数据都存到溢出页中,真实数据存储20字节大小的指向溢出页地址。

我的微信公众号
我的公众号