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行格式下,行存储结构如下:
- 变长字段长度列表
- NULL值标志位
- 事务ID和回滚指针
- 列数据
源码中行的处理主要在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;
页的主要组成部分:
- 文件头(Fil Header) - 38字节
- 页头(Page Header) - 56字节
- 最小和最大记录(Infimum + Supremum)
- 用户记录(User Records)
- 空闲空间(Free Space)
- 页目录(Page Directory)
- 文件尾(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);
区分为几种类型:
- FREE: 完全空闲的区
- FREE_FRAG: 有部分空闲页的区
- FULL_FRAG: 没有空闲页的区
- 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(64个16KB页)
↓
页(Page) → 16KB
↓
行(Row) → 实际数据记录
源码中的关键函数
- 行操作:
storage/innobase/row/row0* - 页操作:
storage/innobase/page/page0* - 区操作:
storage/innobase/fsp/fsp0* - 段操作:
storage/innobase/fsp/fsp0fsp.cc - 表空间:
storage/innobase/fil/fil0fil.cc
这种层次化的设计使得InnoDB能够高效地管理磁盘空间,同时提供良好的性能。
本文来自博客园,作者:NeoLshu,转载请注明原文链接:https://www.cnblogs.com/neolshu/p/19120865

浙公网安备 33010602011771号