文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

MySQL存储结构详解:段、区、页、行

MySQL存储结构详解:段、区、页、行

MySQL的InnoDB存储引擎采用了一种层次化的存储结构,包括表空间(tablespace)、段(segment)、区(extent)、页(page)和行(row)。下面我将结合源码(基于MySQL 8.0)详细解释这些概念。

1. 行(Row)

行是InnoDB存储数据的最小逻辑单位,对应表中的一行记录。

行格式

InnoDB支持多种行格式(由innodb_default_row_format控制):

  • COMPACT
  • DYNAMIC(默认)
  • COMPRESSED
  • REDUNDANT

在源码中,行格式的定义主要在storage/innobase/include/row0types.h中:

/* Precise data types */
typedef byte dict_index_t;
typedef byte dict_table_t;
typedef byte dtuple_t;    /* data tuple(row) */
typedef byte dfield_t;    /* data field */

行存储结构

在DYNAMIC行格式下,行存储结构如下:

  1. 变长字段长度列表
  2. NULL值标志位
  3. 事务ID和回滚指针
  4. 列数据

源码中行的处理主要在storage/innobase/row/目录下,如row0mysql.cc中处理行与MySQL格式的转换。

InnoDB行格式:

Compact、Redundant、Dynamic(默认是这个行结构)和Compressed

Compact

变长字段长度列表:统计可变长字段内容对应的字节(分块、按字段顺序存储)

NULL标志位:标志每一位是不是为空

例子:

有报错

  • 原因:每一行最大的占用空间确实是65535个字节,但是由于c是可变长且需要标志是否为空,所以需要在每一行开辟出变长字段长度列表和NULL标志位。所以sql不能够执行成功。

65532就可以了

变长1字节,NULL2字节

create test(
c varchar(65535)
) CHARSET = ascii ROW_FORMAT = Compact

记录头信息:里面有个字段:next_record:指向下一行的地址

行溢出(Compact、Dynamic)

假如一个页存不下一整行的数据,行结构为Compact、Redundant

  • 会把一部分数据和下一页的地址(20个字节)存在第一页,其他数据再放在第二页,以此类推。

Dynamic则是会

  • 在第一页存放下一个数据页的地址,不放数据。

Compressed

  • 会用压缩算法对数据进行压缩

2. 页(Page)

页是InnoDB磁盘管理的最小单位,默认大小为16KB(可通过innodb_page_size配置)。

页结构

在源码storage/innobase/include/page0types.h中定义了页的基本结构:

/** File page typedef */
typedef byte page_t;

页的主要组成部分:

  1. 文件头(Fil Header) - 38字节
  2. 页头(Page Header) - 56字节
  3. 最小和最大记录(Infimum + Supremum)
  4. 用户记录(User Records)
  5. 空闲空间(Free Space)
  6. 页目录(Page Directory)
  7. 文件尾(Fil Trailer) - 8字节

页类型

storage/innobase/include/fil0types.h中定义了多种页类型:

/** Page types */
enum page_type_t {
    FIL_PAGE_INDEX = 17855,       /* B-tree node */
    FIL_PAGE_UNDO_LOG = 2,        /* Undo log page */
    FIL_PAGE_INODE = 3,           /* Index node */
    FIL_PAGE_IBUF_FREE_LIST = 4,  /* Insert buffer free list */
    // ... 其他类型
};

3. 区(Extent)

区是由连续的页组成的物理结构,每个区默认包含64个页(16KB * 64 = 1MB)。

区的管理

在源码storage/innobase/fsp/fsp0fsp.cc中管理区的分配和释放:

/** Allocates a new free extent.
@param[in]      space           tablespace
@param[in]      hint            hint of which extent would be desirable: any page
                                in the extent
@param[in]      file            file name
@param[in]      line            line where called
@param[in,out]  mtr             mini-transaction
@return extent descriptor, NULL if cannot be allocated */
buf_block_t *
fsp_alloc_free_extent(
    fil_space_t *space,
    page_no_t   hint,
    const char *file,
    ulint       line,
    mtr_t       *mtr);

区分为几种类型:

  1. FREE: 完全空闲的区
  2. FREE_FRAG: 有部分空闲页的区
  3. FULL_FRAG: 没有空闲页的区
  4. FSEG: 属于某个段的区

4. 段(Segment)

段是逻辑上的概念,由一个或多个区组成。每个索引有两个段:

  • 叶子节点段(leaf segment)
  • 非叶子节点段(non-leaf segment)

段的管理

在源码storage/innobase/include/fsp0types.h中定义了段的结构:

/** File segment header data structure */
struct fseg_header_t {
    /* space id of the inode */
    space_id_t space;
    /* page number of the inode */
    page_no_t page;
    /* byte offset of the inode */
    ulint offset;
};

段的管理主要在storage/innobase/fsp/fsp0fsp.cc中:

/** Creates a new segment.
@param[in]      space_id        space id
@param[in]      page            page number
@param[in]      byte_offset     byte offset of the created segment header
@param[in]      file            file name
@param[in]      line            line where called
@param[in,out]  mtr             mini-transaction
@return the block where the segment header was created, NULL if could not
create segment because of lack of space */
buf_block_t *
fseg_create_general(
    space_id_t      space_id,
    page_no_t       page,
    ulint           byte_offset,
    const char *    file,
    ulint           line,
    mtr_t *         mtr);

5. 表空间(Tablespace)

表空间是段的集合,分为:

  • 系统表空间(ibdata1)
  • 独立表空间(每个表一个.ibd文件)
  • 通用表空间
  • 临时表空间

在源码storage/innobase/include/fsp0types.h中定义了表空间结构:

/** Tablespace memory object */
struct fil_space_t {
    // ... 各种成员变量
};

层级关系总结

表空间(Tablespace)
    ↓
段(Segment) → 叶子段/非叶子段
    ↓
区(Extent)1MB(6416KB页)
    ↓
页(Page)16KB
    ↓
行(Row) → 实际数据记录

源码中的关键函数

  1. 行操作:storage/innobase/row/row0*
  2. 页操作:storage/innobase/page/page0*
  3. 区操作:storage/innobase/fsp/fsp0*
  4. 段操作:storage/innobase/fsp/fsp0fsp.cc
  5. 表空间:storage/innobase/fil/fil0fil.cc

这种层次化的设计使得InnoDB能够高效地管理磁盘空间,同时提供良好的性能。

posted @ 2025-08-27 13:00  NeoLshu  阅读(10)  评论(0)    收藏  举报  来源