⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 飞天小牛肉 「小牛肉」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

MySQL 怎么存储,其实问的是 InnoDB 怎么存储,如果你清楚 InnoDB 存储引擎一行记录的格式,那么关于 NULL 值怎么存放这个问题,其实还是很简单的。

InnoDB 逻辑存储结构

InnoDB 中的所有数据都被逻辑地存放在一个空间中,这个空间被称之为 “表空间”(tablespace),表空间可以看作 InnoDB 存储引擎逻辑结构的最顶层,其中包含以下几个部分:

  • 段(segment):表空间是由各个段组成的,常见的段有:

    • 数据段(leaf node segment):InnoDB 是索引组织表(index organized)的,数据即索引,索引即数据,因此数据段其实就是索引(B+ 数的叶子结点)
    • 索引段(non-leaf node segment):B+ 树的非叶子节点
    • 回滚段(rollback segment):也就是 undo log 中的数据
  • 区(extent):一个段最多可以申请 4 个区,区由 64 个连续的页组成,每个页大小为 16KB,即每个区的大小为 64 * 16 = 1M

  • 页(page),也称为 “块”(block):页是 InnoDB 磁盘管理的最小单位。每个页中的数据组织形式是 “行(row)”,也就是说数据是按行进行存放的,每个页最多存放 16KB / 2 ~ 200 = 7992 行的记录

InnoDB 行记录格式

InnoDB 存储引擎提供了 Compact 和 Redundant 两种格式来存放行记录格式,MySQL 5.1 默认保存为 Compact 格式。

可以通过 show table status like 'table_name' 来查看当前表使用的行记录格式。

Redundant 是为了兼容 MySQL 5.0 之前的版本来存在的,这里就不详细说了,主要看 Compact 格式,如下所示:

首先,Compact 行格式的首部是一个非 NULL 变长字段(varchar)的长度列表,并且这个长度列表是按照列的顺序逆序放置的:

  • 当列的长度 < 255 字节,用 1 个字节标识
  • 当列的长度 > 255 字节,用 2 个字节标识

举个例子,比如有四个字段 (id, name, age, address),name 和 address 都是变长类型,有两行记录:

id name age address
1 Job 20 Los Angeles
2 Julia 21 Panama
  • name 列的长度分别是 3 和 5,十六进制表示 0x03 和 0x05
  • address 列的长度分别是 11 和 6,十六进制表示 0xB 和 0x06

这两行记录对应的 Compact 格式如下图所示:

第二个部分就是 NULL 标识位,用于指示这行数据中是否有 NULL 值,若有的话,则将对应的比特位置为 1。具体来说,每个列对应一个二进制位(bit),二进制位同样按照列的顺序逆序排列。

比如有一行记录 id = 1, name = "admin", age = NULL,那么这行记录对应的 NULL 标位就是 100(逆序排放,[age, name, id]),然后在高位补 0,最终就是 0000 0100

第三部分是记录头信息(record header),固定占用 5 个字节(40 位),主要就是包含比如该行是否已被删除、页中下一条记录的相对位置等等之类的。

文章目录
  1. 1. InnoDB 逻辑存储结构
  2. 2. InnoDB 行记录格式