【MySQL】数据存储格式

23 阅读5分钟

存储路径

-- 数据存储位置,默认/var/lib/mysql/
SHOW VARIABLES LIKE 'datadir';
  • /var/lib/mysql/

    • databasename/:每个数据库都有一个文件夹

      • tablename.idb:存储表数据
    • binlog.00000x:日志文件

    • performance_schema/:

表空间文件

  • 组成

    • 段(segment)

      • 索引段:B + 树的非叶子节点的区的集合;
      • 数据段:B + 树的叶子节点的区的集合;
      • 回滚段:回滚数据的区的集合,实现MVCC
    • 区(extent):索引分配空间的单位,1MB

      • B+树每一行是双向链表
      • 链表中相邻的页的物理位置也相邻
    • 页(page):数据最小读写单位,16KB

    • 行(row):一行数据

行格式

  • Redundant:MySQL 5.0 版本之前,不够紧凑。

  • Compact:一种紧凑的行格式,MySQL 5.1 版本之后默认设置。

    • 记录的额外信息

      • 变长字段长度列表:只出现在有varchar、text、blob等变长字段的行,记录占用的字节数

        • 按字段顺序逆序排列:「记录头信息」中指向下一个记录的指针指向的是下一条记录的「记录头信息」和「真实数据」之间的位置,好处是向左读就是记录头信息,向右读就是真实数据。使得位置靠前的记录的真实数据和数据对应的字段长度信息可以同时在一个 CPU Cache Line 中,这样就可以提高 CPU Cache 的命中率。
        • 不需要保存值为 NULL 的变长字段的长度。
        • 长度<255 1字节,<65535 2字节。
      • NULL 值列表:只出现在当有字段可为NULL时,记录是否为NULL

        • 每个允许 NULL 值的列对应一个bit,逆序排列
        • 1代表该列的值为NULL,0为非NULL
        • 不足 8 位要在高位补 0
      • 记录头信息

        • delete_mask :标识此条数据是否被删除。删除记录标记为 1。
        • next_record:下一条记录的位置。
        • record_type:当前记录的类型,0表示普通记录,1表示B+树非叶子节点记录,2表示最小记录,3表示最大记录
    • 记录的真实数据

      • row_id:既没有指定主键,又没有唯一约束,为记录添加 row_id 隐藏字段。6字节
      • trx_id:事务id,表示数据是由哪个事务生成的。必需的,占用 6 个字节。
      • roll_pointer:这条记录上一个版本的指针。必需的,占用 7 个字节。
  • Dynamic 和 Compressed:都是紧凑的行格式,基于 Compact 改进。从 MySQL5.7 版本之后,默认使用 Dynamic 行格式。

varchar(n) 中 n 最大取值为多少?

MySQL 规定除了 TEXT、BLOBs 这种大对象类型之外,其他所有的列(不包括隐藏列和记录头信息)占用的字节长度加起来 <= 65535 个字节。

varchar(n) :n 代表的是最多存储的字符数量,字母1字节,汉字2字节

N = (65535-变长字段列表长度-NULL值列表长度-)/单字符最多占用字节数【assic 1,utf-8 3】

行溢出

Why?

大对象如 TEXT、BLOB 可能存储更多的数据,这时一个页可能就存不了一条记录。这个时候就会发生行溢出,多的数据就会存到另外的「溢出页」中。

Compressed 和 Dynamic采用完全的行溢出方式,记录的真实数据处不会存储该列的一部分数据,只存储 20 个字节的指针来指向溢出页。实际的数据都存储在溢出页中。

数据页

数据库的 I/O 操作的最小单位是页,InnoDB 数据页的默认大小是 16KB,主要作用是存储数据。

  • File Header:文件头,表示页的信息

    • 有两个指针,分别指向上一个数据页和下一个数据页,连接起来的页相当于一个双向的链表
  • Page Header:页头,表示页的状态信息

  • Infimum+supremum:两个虚拟的伪记录,分别表示页中的最小记录和最大记录

  • User Records:用户记录,存储行记录内容

  • Free Space:空闲空间,页中还没被使用的空间

  • Page Directory:页目录,由多个槽组成,存储用户记录的相对位置,槽相当于分组记录的索引。

    • 二分法快速定位要查询的记录在哪个槽,再遍历槽内的所有记录,找到对应的记录。
  • File Tailer:文件尾,校验页是否完整

组织数据

  • 数据页中的记录按照「主键」顺序组成单向链表

    • 单向链表的特点就是插入、删除非常方便
    • 页目录,起到记录索引的作用

页目录创建的过程:

  1. 将所有的记录划分成几个组,这些记录包括最小记录和最大记录,但不包括标记为“已删除”的记录;
  2. 每个记录组的最后一条记录就是组内最大的那条记录,并且最后一条记录的头信息中会存储该组一共有多少条记录,作为 n_owned 字段(上图中粉红色字段)
  3. 页目录用来存储每组最后一条记录的地址偏移量,这些地址偏移量会按照先后顺序存储起来,每组的地址偏移量也被称之为槽(slot),每个槽相当于指针指向了不同组的最后一个记录。

分组中数据的数目:

  • 第一个分组中的记录只能有 1 条记录;
  • 最后一个分组中的记录条数范围只能在 1-8 条之间;
  • 剩下的分组中记录条数范围只能在 4-8 条之间。