前言
大家好,我是 xicheng。从这篇文章开始,会陆续地更新 MySQL 相干的相干文章。帮忙大家晋升根底的同时,顺便就筹备了面试的八股文,开始发车。
常见存储引擎
在讲 InnoDB 之前先看一下 MySQL 有哪些常见的存储引擎。
InnoDB:反对事务,行锁设计,反对外键,通过 MVCC 获取高并发性,5.5.8 开始成为 MySQL 的默认存储引擎。提供插入缓存,二次写,自适应哈希索引,预读等高性能,高可用性能。次要面向 OLTP(在线事务处理)利用。表都是依据主键程序组织寄存的,这种形式叫索引组织表。
MyISAM:不反对事务,存储引擎表由 MYD 与 MYI 组成,前者存储数据文件,后者存储索引文件。次要面向 OLAP(联机剖析解决)利用。
Memory:数据放内存中,速度十分快,只反对表锁,并发性能差,索引默认应用哈希索引。
Archive:只反对 SELECT 与 INSERT。适宜存储归档数据。
NDB:集群存储引擎,数据全放内存中,JOIN 操作是在 Server 层实现而不是存储引擎层。
Federated:不存放数据,指向近程数据库上的表。只反对 MySQL 数据库表,不反对其它数据库的表。
抉择适合的存储引擎
须要反对事务,热备份,疾速的解体复原就抉择 InnoDB。
不须要反对事务,根本只有 INSERT 与 SELECT,例如日志表。能够抉择 MyISAM。
行格局
包含 COMPACT,REDUNDANT,DYNAMIC(MySQL8.0 默认格局),COMPRESSED
InnoDB1.0.x 版本的文件格式定义为 Barracuda,之前的版本定义为 Antelope。Barracuda 文件格式蕴含了 Antelope 文件格式,并引入了 DYNAMIC,COMPRESSED 两种行格局。如下图所示。
COMPACT
行构造示意图
-
变长字段长度列表
- 存的是各变长字段的实在数据占用的字节数。并按字段程序逆序寄存。
- 当 maxlen(可变字段一个字符所需的字节数)* m(该字段最多存储的字符数)> 255,且 L(实在存的字节数)> 127 时,变长字段应用 2 字节记录长度。否则应用一个字节记录长度。
-
NULL 值列表
- 每列占用一个二进制位,依照字段程序逆序排序,值为 1 示意该列的值为 NULL。值为 0 示意该列的值不为 NULL。
- NULL 值列表必须用整数字节示意,若应用的二进制位数有余整数字节时,则在高位补 0。
- 记录头信息,固定由 5 个字节组成,如下表所示。
名称 | 大小(位) | 形容 | |
---|---|---|---|
预留位 1 | 1 | 没有应用 | |
预留位 | 21 | 没有应用 | |
deleted_flag | 1 | 标识该条记录是否被删除 | |
min_rec_flag | 1 | B+ 树中每层非叶子节点的最小的目录项都会增加该标记 | |
n_owned | 4 | 1 页记录会被分为若干组,每组中有一条记录的该值代表该组中所有记录的条数。其余记录的该值为 0 | |
heap_no | 13 | 以后记录在页面堆中的绝对地位 | |
record_type | 3 | 0:一般记录,1:B+ 树非叶子节点的目录条数,2:Infimum 记录(下边界,记录比该页中任何主键值都要小的值),3:Supremum 记录(上边界,记录比该页中任何主键值都要大的值,形成了页中记录的边界) | |
next_record | 16 | 从以后记录的实在数据到下⼀条记录的实在数据的地址偏移量。这样向左读取就是记录头信息,向右读取就是实在数据。 |
行记录的实在数据
除了有用户定义的列外,还有若干暗藏列,如下表所示。
列名 | 是否必须 | 大小(字节) | 形容 |
---|---|---|---|
DB_ROW_ID | 否 | 6 | 行 id。如果⽤户没有定义主键,则取⼀个 Unique 键作为主键,如果表中无 Unique 键,InnoDB 会默认增加⼀个 DB_ROW_ID 暗藏列作为主键 |
DB_TRX_ID | 是 | 6 | 事务 idDB_ROLL_PTR 是 7 回滚指针 |
DB_ROLL_PTR | 是 | 7 | 回滚指针 |
行记录的实在数据的程序一次是:DB_ROW_ID,DB_TRX_ID,DB_ROLL_PTR,用户定义的列 1 的值,用户定义的列 2 的值,…,用户定义的列 n 的值。其余行格局的实在数据也是这个程序。
CHAR 存储格局
当采纳定长编码的字符集时,该列用的字节数不会被加到变长字段长度列表中。采纳变长编码的字符集时,则会加到其中。
采纳变长编码字符集时,CHAR(M)至多会占用 M 个字节。
REDUNDANT
行构造示意图
行记录的额定信息
-
字段长度偏移列表
依照相邻两个偏移量的差值来计算各个列值的长度。并按字段程序逆序寄存。
比方,某一行的字段长度偏移列表为 18 15 0D 07,因为是逆序寄存的,则按列排序就是 07 0D 15 18。
- 第 1 列长度就是 0x07 个字节,即 7 个字节。
- 第 2 列长度就是(0x0D – 0x07)个字节,即 6 个字节。
- 第 3 列长度就是(0x15 – 0x0D)个字节,即 8 个字节。
- 第 3 列长度就是(0x18 – 0x15)个字节,即 3 个字节。
- 记录头信息,固定由 6 个字节组成,如下表所示。
名称 | 大小(位) | 形容 | |
---|---|---|---|
预留位 1 | 1 | 没有应用 | |
预留位 2 | 1 | 没有应用 | |
deleted_flag | 1 | 标识该条记录是否被删除 | |
min_rec_flag | 1 | B+ 树中每层非叶子节点的最小的目录项都会增加该标记 | |
n_owned | 4 | 1 页记录会被分为若干组,每组中有一条记录的该值代表该组中所有记录的条数。其余记录的该值为 0 | |
heap_no | 13 | 以后记录在页面堆中的绝对地位 | |
n_field | 10 | 记录中列的数量 | |
byte_offs_flag | 1 | 标记字段长度偏移列表中每个列对应的偏移量应用一个字节还是两个字节来示意 实在数据占用空间大小为 n 当 n <=127 时,值为 1,用 1 个字节示意偏移量 当 127<n<=2^15^ 时,值为 0,用 2 个字节示意偏移量 当 n >2^15^ 时,值为 0,记录的一部分已放入溢出区,用 2 个字节示意偏移量 |
|
next_record | 16 | 从以后记录的实在数据到下⼀条记录的实在数据的地址偏移量。这样向左读取就是记录头信息,向右读取就是实在数据 |
- NULL 值解决
如果列对应的偏移量值的第 1 个比特位为 1,则该列值就是 NULL,否则就不是 NULL。
如果存储 NULL 值的字段是定长类型,如 CHAR(M),则字段对应的实在数据会应用 0 填充。
如果存储 NULL 值的字段是变长类型,如 VARCHAR(M), 则不在行记录的实在局部占用任何的存储空间。
CHAR 存储格局
CHAR(M)占用空间 = 该字符集示意一个字符最多须要的字节数 * M。例如,utf- 8 的 CHAR(10)占用 30 个字节。gbk 的 CHAR(10)占用 20 个字节。
溢出列
构造示意图如下。
每页额定须要 132 个字节存储其它数据。每条记录须要固定用 27 个字节存储存储额定信息。
每页至多须要存 2 条记录。则有如下不等式:132 + 2 * (27 + n) < 16KB。解出 n < 8099。当这列中存储的数据占用空间 >=8099 字节时,就会成为溢出列。
DYNAMIC
与 COMPACT 相似,区别是,解决溢出列时,会把该列的所有数据都存到溢出列中。
COMPRESSED
与 DYNAMIC 相似,区别是,会采纳压缩算法对页面进行压缩。
结尾
MySQL 通过 InnoDB 的记录行开了个头,心愿大家能继续关注上来。下一篇 MySQL 文章讲 InnoDB- 数据页的存储构造。
微信扫描下方二维码,或搜寻“xicheng”,关注公众号后回复【笔记】,有我筹备的 15 万字 Java 面试笔记。
感激各位人才的点赞、珍藏和评论,干货文章继续更新中,下篇文章再见!