conanan's blog conanan's blog
首页
关于
  • 分类
  • 标签
  • 归档
  • Java
  • Java Web
  • 工具

    • Maven
  • MySQL
  • Redis
  • Git
  • Vim
  • Nginx
  • Docker
GitHub

Evan Xu

前端界的小学生
首页
关于
  • 分类
  • 标签
  • 归档
  • Java
  • Java Web
  • 工具

    • Maven
  • MySQL
  • Redis
  • Git
  • Vim
  • Nginx
  • Docker
GitHub
  • 基础

  • 高级

    • MySQL优化
    • MySQL 的系统架构
    • 高级
    • InnoDB
      • InnoDB 页
      • InnoDB 行格式
        • 指定行格式语法
        • Compact 行格式
        • 变长字段长度列表
        • NULL 值列表
        • 记录头信息—固定5字节
  • 运维

  • 练习

  • MySQL
  • 高级
conanan
2021-02-03

InnoDB

# InnoDB

  • MySQL 服务器中负责对表中的数据进行读取和写入工作的部分是存储引擎,如 InnoDB

# InnoDB 页

真正处理数据的过程发生在内存中,所以需要把磁盘中的数据加载到内存中。如果是写入或修改操作,还需把内存中内容刷新到磁盘上。

读写磁盘速度非常慢,与读写内存差了几个数量级。所以 InnoDB 采取的方式是:将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位。InnoDB 中页的大小一般为 16KB,也就是一次最少从磁盘中读取 16KB 的内容到内存中,一次最少把内存中 16KB 的内容刷新到磁盘中。

提示

系统变量 innodb_page_size 表明了 InnoDB 存储引擎中页的大小,默认值为 16384 Byte,即 16KB。且该系统变量只能在第一次初始化 MySQL 数据目录时指定,之后就再也不能修改了。初始化时可以通过 mysqld-initialize 来初始化数据目录

# InnoDB 行格式

我们平时以记录为单位向表中插入数据,这些记录在磁盘上的存放形式也被称为行格式或者记录格式。InnoDB目前有四种行格式:Dynamic、Compressed、Compact、Redundant(这个在 Navicat 里没有,太老了,5.0 设计出来的)目前 MySQL5.7以及8的默认行格式为 Dynamic。其原理都大致相同。

# 指定行格式语法

create table 表名 (列的信息) row_format=行格式名称;

alter table 表名 row_format=行格式名称;
1
2
3

# Compact 行格式

# 变长字段长度列表

创建表

CREATE TABLE record_format_demo
(
    c1 VARCHAR(10),
    c2 VARCHAR(10) NOT NULL,
    c3 CHAR(10),
    c4 VARCHAR(10)
) CHARSET = ASCII
  ROW_FORMAT = COMPACT;
1
2
3
4
5
6
7
8

注意,该表的字符集为 ASCII,只能插入空格、标点符号、数字、大小写字母和一些不可见字符

插入2行记录

INSERT INTO record_format_demo (c1, c2, c3, c4)
VALUES ('aaaa', 'bbb', 'cc', 'd'),
       ('eeee', 'fff', NULL, NULL);
1
2
3

由于该表的字符集为 ASCII,所以每列的真实数据只需一B即可保存,且c1,c2,c4列需要将存储空间字节数逆序保存到变长字段长度列表中。

第一行c1,c2,c4列的数据长度为4,3,1,逆序并已16进制表示则为 0x01, 0x03, 0x04。存入compact行格式中为:010304

第二行存入行格式中数据为0304

提示

并不是所有记录行都有这个变长字段长度列表,如果表中所有列都不是变长的,或者所有列的值都是 NULL 的话(不存储值为 NULL 的列),则不存在该表了

这几条记录中数据内容都比较短,占用字节数少,所以变长字段长度列表中每列只需1个字节即可,但是若内容多,则需要2个字节来存储长度,具体InnoDB 有如下规则(引入W、M、L这几个符号):

  • 假设某个字符集中最多需要W字节来表示一个字符(即 show charset 语句结果中的 maxlen 列)。比如 utf8mb4字符集中的W就是4,utf8字符集中的W就是3,gbk字符集中的W就是2,ascii字符集中的W就是1
  • 对于变长类型VARCHAR(M)来说,这种类型表示能存储最多M个字符。所以该类型能表示的字符串最多占用的字节数就是 WxM
  • 假设该变长字段实际存储的字符串占用的字节数是L。

则确定使用1字节还是2字节表示变长字段真实数据占用字节数规则就是:

  • 如果WxM<=255,那么使用1字节表示

    InnoDB在读取变长字段长度列表时先查看表结构,直接计算WxM<=255?为真则认为只是用1字节表示。直接使用1字节的二进制表示即可

  • 如果WxM>255,则分如下情况:

    • 若L<=127,则使用1字节表示
    • 若L>127,则使用2字节表示

    InnoDB在读取变长字段长度列表时先查看表结构,直接计算WxM<=255?为假。那么如何区分正在读取的某个字节是一个单独的字段长度还是半个字段长度呢?(借助127规则)InnoDB 使用该字节的第一个二进制位作为标志位。为0则该字节为单独的字段长度(即L<=127本来就是那么点),为1则是半个字段长度,这个仅作为标志位,不参与运算哦

提示

对一条记录来说,若某个字段占用的字节数特别多,InnoDB有可能把该字段值的一部分数据存放到所谓的溢出页中。那么该字段在记录的变长字段长度列表出只存储在本页面中的长度,使用2字节即可。至于溢出页字段长度则不做讨论

# NULL 值列表

存储null字符串到真实数据中占用地方,所以Compact 行格式将其统一管理:

  • 先统计表中允许存储 NULL 的列有哪些
  • 若表中没有允许存储 NULL 的列,则NULL 值列表也不存在了。否则将每个允许存储NULL的列对应一个二进制位,并按照列的逆序排列,二进制意义如下:
    • 为1则代表该列值为NULL
    • 为0则代表该列值不为NULL
  • MySQL规定NULL 值列表必须用整数个字节的位表示,不足一个字节在高位补0

对于上述表中2条数据来说,只有c1,c3,c4可以存储NULL,则NULL 值列表如下:

第一行:先逆序排序为c4,c3,c1,高位补0,最终十六进制值为 0x00

第二行:先逆序排序为c4,c3,c1,高位补0,二进制为110,最终十六进制值为 0x06

# 记录头信息—固定5字节

由固定5字节组成,即40个二进制位

编辑
上次更新: 2021/02/09, 10:14:42
高级
MySQL

← 高级 MySQL→

最近更新
01
线程生命周期
07-06
02
线程安全理论
06-24
03
并发简史
06-24
更多文章>
Theme by Vdoing | Copyright © 2019-2021 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×