【大白话系列】MySQL 学习总结 之 COMPACT 行格式的设计原理
如果大家对我的 【大白话系列】MySQL 学习总结系列 感兴趣的话,可以点击关注一波。
一、回顾
MySQL 学习总结系列至此已经第七节了。
从大方向:我们已经学习了 MySQL 的架构设计、InnoDB 的架构设计。
从较为深入的:我们已经学习了 rodo log 和 binlog 配合的两阶段提交协议,了解 缓冲池的设计原理和支持高并发、动态调整的管理机制。
下面,我们将介绍数据行格式:数据是以什么格式存储在数据页中的。
二、行存储格式
InnoDB 储存引擎支持有四种行储存格式:COMPACT、Redundant、Dynamic 和 COMPRESSED。
下面我们将重点介绍 COMPACT 行格式:
COMPACT 行存储格式大概类似这样:
变长字段的长度列表,null值列表,数据头,column01的值,column02的值,column0n的值......
ps:为了让磁盘空间得到最大的利用率,每个数据行都是紧紧地挨在一起的。
下面我们将详细介绍 COMPACT 行格式的各个知识点,当你学习完之后,你就晓得即使每行数据紧紧地挨在一起,MySQL 也能精准地将每行数据找出来~
三、变长字段如何存储?
1、变长字段的存储问题
我们都知道,varchar 类型是变长的,例如 varchar(50),那么这个字段值的长度范围:0 ~ 50 个字符。但是,不是每个字段值都刚好50个字符,肯定会有的长有的短。
那么,数据存储时,会按照字段定义时的最大长度来存储值吗?
必须不会的,如果都按照最大长度存储,当出现值不满 50个字符长度时,会浪费磁盘空间和内存空间。
为什么也浪费内存空间,数据不是存放在磁盘么?大家不会忘了缓冲池的作用了吧?哈哈,要记得缓冲池和磁盘数据交换的单位就是数据页而数据行是存放在数据页中的
2、变长字段长度列表
InnoDB 中,利用 变长字段长度列表 来解决上面的问题:
- 变长字段长度列表记录每一个变长字段值的长度,存储的长度是十六进制的。
- 如果有多个变长字段,那么变长字段长度列表是按逆序存储的。
下面用一个例子来描述一下变长字段长度列表的使用原理:
-- 表结构
create table test(
c1 varchar(10) comment '字段1-变长',
c2 varchar(5) comment '字段2-变长',
c3 varchar(20) comment '字段3-变长',
c4 char(1) comment '字段4-定长',
c5 char(1) comment '字段5-定长'
) ENGINE=InnoDB;
-- 一行数据
insert into test values('hello','ni','hao','a','a');
我们来算一下他们的长度(十六进制):
- hello 的长度为5,十六进制为 0x05
- ni 的长度为2,十六进制为 0x02
- hao 的长度为3,十六进制为 0x03
那么,实际的存储格式是这样的:
0x03 0x02 0x05 null值列表 数据头 hello hi hao a a
四、NULL 值字段如何存储?
1、可为 NULL 字段的存储问题
定义为 default NULL 的字段,值可空可不空。那如果字段值为 NULL,数据行里是怎样存储的呢?是直接存储“NULL”字段吗?
我们分析一下:
- 如果是,那将会浪费磁盘空间,本来值就是 NULL 的,你现在给我搞了个四个字符大小的字符串。
- 如果不是,那怎么识别这个字段是否是 NULL 呢?
2、NULL值列表
InnoDB 中,利用 NULL值列表 来解决上面的问题:
- NULL 值列表记录可为 NULL 的字段的情况。
- 用二进制bit位来标识字段值是否为 NULL。1为 NULL,0 不为 NULL。
- 如果有多个可为 NULL 的字段,那么 NULL 值列表也是按照逆序存储的。
- 而且 NULL 值列表的位数必须是 8bit 的N倍。例如:列表仅仅只有4个bit,则往高位补0,补到 8个bit。
下面用一个例子来描述一下 NULL 值列表 的使用原理:
-- 表结构
create table test(
c1 varchar(10) not null comment '字段1-变长',
c2 varchar(5) comment '字段2-变长',
c3 char(1) comment '字段3-变长',
c4 varchar(30) comment '字段4-定长',
c5 varchar(50) comment '字段5-定长'
) ENGINE=InnoDB;
-- 一行数据
insert into test values('howinfun',null,'m',null,'foshan');
算一下变长字段的长度:
- howinfun 的长度为8,十六进制为 0x08
- foshan 的长度为6,十六进制为 0x06
统计一下值为 NULL 的字段:
- c2 字段为 NULL
- c4 字段为 NULL
那么,实际的存储格式是这样的:
0x06 0x08 00000101 数据头 howinfun m foshan
3、采用 NULL值列表 和 直接存储“NULL”字符串相比,有多大的存储差距?
到此,我们就可以算一下这两种方案的存储差距有多大了。
- 一个字节 8个bit,NULL 值列表用二进制 bit 位来标识字段值是否为 NULL;那么就是说,标识8个字段才占用一个字节。
- 而如果用字符串的方式来存储,而一个"NULL"字符串足足用了四个字节(英文一个字符等于一个字节,中文一个字符等于两个字节),那么同样的8个字段就需要36个字节了。
这差距是非常明显的!
五、数据头
COMPACT 行格式中,除了 变长字段长度列表 和 NULL 值列表,就到数据头了。
数据头的大小为 40 个bit位。
下面介绍 40个 bit 分别都有什么信息。
| 名称 | 大小 (bit) | 描述 |
|---|---|---|
| 预留位1 | 1 | 没有使用 |
| 预留位2 | 1 | 没有使用 |
| delete_mask | 1 | 标记该记录是否被删除 |
| min_rec_mask | 1 | B+树里每一层的非叶子节点里的最小值都有这个标记 |
| n_owned | 4 | 表示当前记录拥有的记录数 |
| heap_no | 13 | 表示当前记录在记录堆的位置信息 |
| record_type | 3 | 标识当前记录的类型:0代表的是普通类型,1代表的是B+树非叶子节点,2代表的是最小值数据,3代表的是最大值数据。 |
| next_record | 16 | 表示下一条记录的相对位置 |
那么,我们看一下,加上数据头的实际存储:
0x06 0x08 00000101 0000000000000000000010000000000000011001 howinfun m foshan
六、一行数据在磁盘是如何存储的
1、字符集编码
上面,我们已经介绍了 COMPACT 行格式了,那么一行数据真正是如何存储的?
我们都知道,在建库和建表时,都可以指定字符集编码。所以,数据都会经过数据库指定的字符集编码后,再进行存储的。
下面用一个例子来描述一下使用原理:
-- 表结构
create table test(
c1 varchar(10) not null comment '字段1',
c2 varchar(5) comment '字段2',
c3 char(1) comment '字段3',
c4 varchar(30) comment '字段4',
c5 varchar(50) comment '字段5'
)
-- 一行数据
insert into test values('howinfun',null,'m',null,'foshan');
假设编码后:
- howinfun 编码后:61616161
- m 编码后:62
- foshan编码后:636363
那么,实际的存储格式是这样的:
0x06 0x08 00000101 0000000000000000000010000000000000011001 61616161 62 636363
2、隐藏字段
除了变长字段长度列表、NULL值列表、40个bit位的数据头和真实数据,其实还包含了一些隐藏字段:
- DB_ROW_ID 字段:如果我们没有指定主键和unique key唯一索引的时候,他就内部自动加一个ROW_ID作为主键。
- DB_TRX_ID 字段:事务 ID,标识这是哪个事务更新的数据
- DB_ROLL_PTR 字段:回滚指针,用来进行事务回滚的
加上隐藏字段后,上面的例子的实际存储可能就是:
0x06 0x08 00000101 0000000000000000000010000000000000011001 00000000094C(DB_ROW_ID)00000000032D(DB_TRX_ID) EA000010078E(DB_ROL_PTR) 616161 636320 6262626262
ps:括号里只是做说明用的,事实是不存在的。
3、行溢出问题
数据页的默认大小是 16kb,但是某些字段的值可以远远大于 16kb。
例如变长字段类型 varchar(N):N 最大可为 65532(65kb),这就远远大于 16kb。
当然了,还有 text 和 blog 字段,这些都是大字段,都可以超过 16kb。
如果一行数据的大小超过了 16kb,就会出现行溢出的现象。
怎么解决?
当一行数据超了 16kb,会在超了大小的那个字段中,可能仅仅包含他的一部分数据,然后同时包含一个20个字节的指针,指向存储了这行数据超了的部分的其他数据页。
【大白话系列】MySQL 学习总结 之 COMPACT 行格式的设计原理的更多相关文章
- 【大白话系统】MySQL 学习总结 之 缓冲池(Buffer Pool) 的设计原理和管理机制
一.缓冲池(Buffer Pool)的地位 在<MySQL 学习总结 之 InnoDB 存储引擎的架构设计>中,我们就讲到,缓冲池是 InnoDB 存储引擎中最重要的组件.因为为了提高 M ...
- MySQL 学习之查漏补缺
1.InnoDB 相关知识点 InnoDB 引擎是将数据划分为若干数据页,页大小一般16 KB,16384个字节. 插入数据是以记录为单位,这些记录在磁盘的存放方式称之为 行格式/记录格式,有 com ...
- J. Cole 的 InnoDB 系列 - 1. 学习 InnoDB - 深入探索核心原理之旅
原文地址:https://blog.jcole.us/2013/01/02/on-learning-innodb-a-journey-to-the-core/,本系列翻译会在其基础上扩展一些 MySQ ...
- MySQL 行格式
以 MySQL 默认的存储引擎 InnoDB 为例 InnoDB 包含以下四种行格式 Compact Redundant Dynamic Compressed 指定行格式 CREATE TABLE 表 ...
- Mysql之InnoDB行格式、数据页结构
Mysql架构图 存储引擎负责对表中的数据的进行读取和写入,常用的存储引擎有InnoDB.MyISAM.Memory等,不同的存储引擎有自己的特性,数据在不同存储引擎中存放的格式也是不同的,比如Mem ...
- InnoDB行格式(compact,redundant)对照
InnoDB行格式分两种格式(COMPACT,redundant)默觉得COMPACT compact的存储格式为 首部为一个非NULL的变长字段长度列表,并且是依照列的顺序逆序放置的,当列的长度小于 ...
- MyISAM和InnoDB的行格式ROW_FORMAT
MyISAM行存储 MyISAM有3种行存储格式:fixed / dynamic / compressed: 格式 说明 备注 fixed 只有当表不包含变长字段(varchar/varbina ...
- 数据页结构 .InnoDb行格式、以及索引底层原理分析
局部性原理 局部性原理是指CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中. 首先要明白局部性原理能解决的是什么问题,也就是主存容量远远比缓存大, CP ...
- 【大白话系统】MySQL 学习总结 之 缓冲池(Buffer Pool) 如何支撑高并发和动态调整
如果大家对我的 [大白话系列]MySQL 学习总结系列 感兴趣的话,可以点击关注一波. 一.上节回顾 在上节< 缓冲池(Buffer Pool) 的设计原理和管理机制>中,介绍了缓冲池整体 ...
随机推荐
- 8.for循环及练习
For循环: 虽然所有循环结构都可以用 while 或者 do...while 表示,但Java提供了另一种语句— —for循环,使一些循环结构变的更加简单. for 循环语句是支持迭代的一种通用 ...
- java异步调用方法
一.利用多线程 直接new线程 Thread t = new Thread(){ @Override public void run() { longTimeMethod(); } }; 使用线程池 ...
- 如梦令编程语言发布 (RML)
如梦令编程语言是在Rebol语言核心语法的基础上,做了一些自以为是的修改而来.谨以此为Rebol语法的传承,略尽绵薄之力. 基本概念 如梦令语言分属Lisp语系,代码本身是一个层层嵌套的Token列表 ...
- CQOI十二省联考游记
Day 0 看似稳如老狗的我实则慌得一逼 看了一上午的CRT,一个字没看进去 我反复安慰自己:我才高一,我才高一 但是,明年的联赛会不会跟今年一样高呢? 明年的心态会不会有现在这么好呢? 明年同届的d ...
- Ninja构建系统入门
1. 介绍 开篇先介绍.先甩资料给大家看,之后再自己演示一下基本使用.Ninja 是Google的一名程序员推出的注重速度的构建工具,一般在Unix/Linux上的程序通过make/makefile来 ...
- WebAPI接口开发实践
背景 在团队两年多陆续负责了几个项目的开发上线已经代码的review,特别是对老项目的重构过程中,发现之前的API设计是没有任何规范和约定的,不同的开发同学有不同的习惯,因此需要一套规范去约定,现在分 ...
- Git详解之常用命令
注意:此篇文章中的绝大部分内容来自摘抄,查阅人员请注意
- 理解Java虚拟机中的栈、堆、堆栈
JAVA的JVM的内存可分为3个区:堆(heap).栈(stack)和方法区(method) 栈区: 每个线程包含一个栈区,栈中只保存方法中(不包括对象的成员变量)的基础数据类型和自定义对象的引用(不 ...
- HanLP《自然语言处理入门》笔记--5.感知机模型与序列标注
笔记转载于GitHub项目:https://github.com/NLP-LOVE/Introduction-NLP 5. 感知机分类与序列标注 第4章我们利用隐马尔可夫模型实现了第一个基于序列标注的 ...
- vue简介,插值表达式,过滤器
目录 VUE框架介绍 what?什么是vue? why?为什么要学习vue? special特点? how如何使用? 下载安装? 导入方式? 挂在点el 插值表达式 delimiters自定义插值表达 ...