MySQL 数据目录和 InnoDB 表空间补充知识:详细结构

本文详细解释了MySQL在Ubuntu系统中的数据目录结构,重点介绍了InnoDB和MyISAM表在文件系统中的表示,包括数据库和表的组织方式、表空间的构成(如页面、区、段和碎片区)、以及系统表和索引的存储机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 数据目录

在Ubuntu下,MySQL的数据目录为/var/lib/mysql

1.1 数据库在文件系统中的表示

(1)创建数据库时,会在数据目录下创建一个与数据库名同名的子目录。(除了information_schema这个系统数据外)

(2)db.opt文件存储数据库的一些属性

MySQL 8.0 之后不再提供

1.2 表在文件系统中的表示

(1)表结构定义。表名.frm文件

(2)表数据

1.2.1 InnoDB

(1)系统表空间。只有一份,默认为数据目录下的ibdata1文件

(2)独立表空间;表名.ibd文件。

MySQL 8.0将ibd文件和frm文件合并,只有一个ibd文件,并且不再提供db.opt文件,其中的字符集、比较规则信息也放在ibd文件中。
存储到系统表空间中的表转移到独立表空间
ALTER TABLE 表名 TABLESPACE innodb_system
存储在独立表空间中的表转移到系统表空间
ALTER TABLE 表名 TABLESPACE innodb_file_per_table

1.2.2 MyISAM

表名.MYD表名.MYI分别表示表的数据文件和索引文件

2. InnoDB的表空间

2.1 页面通用部分

每个页都包含了File HeaderFile Trailer

(1)FIL_PAGE_OFFSET表空间下描述页号,4字节,因此一个表空间最多 64TB,即 (2^32)*16KB

(2)FIL_PAGE_PREVFIL_PAGE_NEXT 上/下页的页号,主要是数据页(INDEX 类型)使用

(3)FIL_PAGE_TYPE据此区分页的类型

2.2 独立表空间结构

表空间划分为一个一个的区(每个 1 MB),每 256 个区组成一个组。
图9-3
(1)第一个组最开始的 3 个页面的类型是固定的。

  1. FSP_HDR:整个表空间中只有一个 FSP_HDR 类型的页面;登记表空间的一些整体属性和本组索引的区。
    图9-9
File Space Header共 112 字节
Space ID4表空间 ID
Not Used4未使用,可忽略
Size4拥有的页面数
FREE Limit4尚未被初始化的最小页号,该字段表示页号之后的区都未被使用,且没有加入到 FREE 链表中
Space Flags4页面大小,是否为共享/临时表空间等属性
FRAG_N_USED4FREE_FRAG 链表中已使用的页面数量
List Base Node for FREE list16FREE 链表基节点
List Base Node for FREE_FRAG list16FREE_FRAG 链表基节点
List Base Node for FULL_FRAG list16FULL_FRAG 链表基节点
Next Unused Segment ID8当前表空间中下一个未使用的 Segment ID,递增
List Base Node for SEG_INODES_FULL list16SEG_INODES_FULL 链表基节点
List Base Node for SEG_INODES_FREE list16SEG_INODES_FREE 链表基节点
  1. INODE:存储 INODE Entry 数据结构

每个段对应的 INODE Entry 结构会存储到 INODE 页中。段特别多的情况下。会有多个 INODE 页,会组成链表。

SEG_INODES_FULL :在该链表中的 INODE 页没有空闲空间
SEG_INODES_FREE :有空闲空间
图9-11
如果新建一个段时,SEG_INODES_FREE 链表为空,需要从表空间的 FREE_FRAG链表申请一个页面,并把页面的类型修改为 INODE。

(2)其余各组最开始的 2 个页面的类型是固定的。

XDES:登记本组 256 个区的属性。

2.2.1 段

一些零散页面以及一些完整区的集合

(1)为什么使用区?

一个区是连续的 64 页。在表中数据量很大时,为某个索引分配空间按照区为单位,这样就尽量让页面链表中相邻的页的物理位置也相邻。

(2)对 B+ 树的叶子节点和非叶子节点进行了区分。会生成一个叶子节点段和一个非叶子节点段(一个索引会生成两个段),段是以区为单位申请存储空间的

(3)以区为单位申请空间,对小表会造成浪费存储空间;提出了碎片区

碎片区只需于表空间,不属于任何段。

碎片区中的页可以用于不同的目的,可以属于不同段。

在刚开始向表中插入数据时,段是从碎片区的以页为单位分配存储空间的,当某个段已经占用了 32 个碎片区页面后,就会以区为单位来分配。(原先占用的碎片区的页面不会被复制到新申请的区中)

2.2.2 区

区可分为 4 种类型,这也是 4 种状态(State)

  1. FREE;空闲的区
  2. FREE_FRAG;有剩余空间页面的碎片区
  3. FULL_FRAG;没有剩余空间页面的碎片区
  4. FSEG;附属于某个段的区

前三种是独立的,直属于表空间

(1)为了方便管理区,设计每个区都对应一个 XDES Entry 的结构(40 字节)。

  1. Segment ID (8 字节);段的 ID,前提是分配给了某个段。
  2. List Node (12 字节);
    指向 前/后一个 XDES Entry 的指针 (Page Number + Offset);
  3. State(4 字节);
  4. Page State Bitmap(16 字节);每个区有 64 页,每页用 2 位表示,表明对应的页是否是空闲。

(2)之所以 XDES Entry 要组成链表,是因为可以把状态分别为 FREE、FREE_FRAG、FULL_FRAG 的区对应的 XDES Entry 结构连接成一个 FREE、FREE_FRAG、FULL_FRAG 链表。

段中数据较少时,首先查看表空间中是否有状态为 FREE_FRAG 的区,如果有,则从该区中取下一个零散页把数据插进去;否则到表空间中申请一个状态为 FREE 的区,把该区的状态变为 FREE_FRAG ,然后从该区中取出一个零散页把数据插入进去。

该区中没有空闲页面后,将其状态变成 FULL_FRAG。

(3)段中数据已经占满了 32 个零散的页后,申请完整的区。

根据段号来区分各个段,对每个段都建立 3 个链表

  1. FREE 链表;所有页面都是空闲的区
  2. NOT_FULL 链表;仍有空闲页面的区
  3. FULL 链表;已经没有空闲页面的区

(4)链表基节点

每个链表对应一个 List Base Node 结构(16字节)

  1. List Length;表明链表一共有多少个节点;
  2. First Node Page;该链表的头结点在表空间中的位置;
  3. Last Node Page;该链表的尾结点在表空间中的位置;
2.2.3 段的结构

每个段定义一个 INODE Entry 结构

  1. Segment ID;对应的段的编号;
  2. NOT_FULL_N_USED;在 NOT_FULL 链表中已经使用了多少个页面;
  3. 3 个 List Base Node;分别为 FREE、NOT_FULL 、FULL 链表基节点
  4. Magic Number
  5. Fragment Array Entry;共有 32 个,每个 Fragment Array Entry 结构对应一个零散的页面,这个结构一共 4 字节,表示一个零散页面的页号。
2.2.4 Segment Header

(1)索引会对应两个段,如何知道某个索引和某个段之间的对应关系?

在数据页的 Page Header 中,属性 PAGE_BTR_SEG_LEAFPAGE_BTR_SEG_TOP在 B+ 树的根页中定义,分别记录了叶子/非叶节点段的头部信息(Segment Header,其中包含表空间ID,页面号,偏移量);

2.3 系统表空间

整个 MySQL 进程只有一个系统表空间,其表空间 ID 为 0。

图9-13

页号页面类型描述
3SYS存储 Change Buffer 的头部信息
4INDEX存储 Change Buffer 的根页面
5TRX_SYS事务相关信息
6SYS第一个回滚段信息
7SYS数据字典头部信息

extent 1 和 extent 2 这两个区,是作为 Doublewrite Buffer;

2.3.1 数据字典

由系统表来记录一些元数据(某个表中有多少列,该表有哪些索引等信息)。这些系统表被称为数据字典,都是以 B+ 树的形式保存在系统表空间的某些页面中。

以下四个为基本系统表
(1)SYS_TABLES;整个 InnoDB 存储引擎中所有表的信息

列名描述
NAME表名
ID(TABLE_ID)表的 ID(每个表具有唯一的 ID)
N_COLS表中列的个数
TYPE类型,记录文件格式、行格式、压缩等信息
MIX_ID忽略
MIX_LEN额外属性
CLUSTER_ID忽略
SPACE所属表空间的 ID

以 NAME 列为主键的聚簇索引;
以 ID 列建立的二级索引;

(2)SYS_COLUMNS;所有列的信息。

列名描述
TABLE_ID该列所属表的 ID
POS表明是第几列
NAME列名
MTYPE列的数据类型(INT、CHAR等)
PRTYPE精确数据类型,修饰主数据类型的,例如是否允许 NULL 等
LEN该列最多占用的字节数
PREC精度,默认为 0

以 (TABLE_ID ,POS)列为主键的聚簇索引

(3)SYS_INDEXES;所有索引的信息。

列名描述
TABLE_ID该索引所属表的 ID
ID(INDEX_ID)索引 ID
NAME索引名
N_FIELDS索引包含几列
TYPE索引类型,例如聚簇索引、唯一二级索引、更改缓冲区的索引、全文索引、普通二级索引
SPACE索引根页面所在的表空间 ID
PAGE_NO索引根页面所在的页面号
MERGE_THRESHOLD指明 B+ 树发生合并时的比例(页面满发生分裂,数据太少发生合并)

以 (TABLE_ID ,ID)列为主键的聚簇索引

(4)SYS_FIELDS;索引对应列的信息。

列名描述
INDEX_ID该列所属索引的 ID
POS该列在索引列中是第几列
COL_NAME对应列的名称

以 (INDEX_ID,POS)列为主键的聚簇索引

(5)由页号为 7 的页面,记录了 Data Dictionary Header。记录上述 4 个表的聚簇索引和二级索引对应的 B+ 树根页面的位置。

图9-14
Max Row ID:隐藏主键的 ID 号,自增并且是全局共享的,即拥有 row_id 列的不同表之间的这个值也都是不同的。(隐藏列 row_id 只有 6 个字节,而此处为 8 字节,因此达到 6 字节上限后,隐藏列 row_id 会重新从 0 开始,在 InnoDB 表中相同隐藏列 row_id 会覆盖之前的数据)
Max Table ID:自增的表 ID 值。
之后记录了这些索引根页面的页号,对应上述 4 个表中的索引。

把数据字典信息当成一个段来分配存储空间,该段只有一个碎片页,即页号为 7 的页。

(6)information_schema 系统数据表

在存储引擎启动时读取这些 SYS 开头的系统表,然后填充到以 INNODB_SYS 开头的表中。

(7)查找过程
首先去页号为 7 的页面,其记录了 Data Dictionary Header,通过它记录的系统表索引根节点,可以查找到系统表中的各个信息;
通过系统表的信息,可以查找到某个索引对应的根结点页面;
根据该索引的根结点页面,可以找到 Segment Header 信息,即可以找到索引对应的叶子节点/非叶节点对应 INODE Entry;

参考书籍

《MySQL 是怎样运行的》

<think>嗯,用户想了解MySQL索引背后的数据结构及其实现算法原理。这个问题很专业,需要从存储引擎层面展开回答,因为MySQL不同存储引擎使用的索引结构不同。 用户可能正在做数据库性能优化,或者在学习数据库底层原理。从问题表述看,用户应该有一定数据库基础,但可能对实现细节不太清楚。需要InnoDBMyISAM这两个最常用引擎的差异。 先梳理核心知识点:B+树作为主流索引结构的原因需要重点解释,包括与B树的对比。哈希索引的适用场景也要说明,毕竟Memory引擎确实在用。对于InnoDB的聚簇索引特性,要用具体例子说明数据存储方式,比如用户表的主键存放位置。 算法层面要分两部分:索引创建过程(比如B+树如何分裂)查询过程(范围查询的叶子节点遍历)。InnoDB的自适应哈希索引是个很好的优化点,应该单独说明其触发机制。 突然想到用户可能混淆"索引实现""数据存储"概念,需要强调聚簇索引中数据行即叶子节点这个特性。MyISAM的分离存储作为对比案例很有必要,可以准备文件偏移量的例子。 索引维护的成本容易被忽视,得补充插入删除时的树平衡操作。最后算法示例部分,伪代码比Python更合适,避免语言特性干扰对本质的理解。 用户可能延伸的问题包括:为什么不用红黑树?联合索引的最左前缀原理如何体现?这些潜在问题可以放在最后的提问环节。不过当前回答已经涉及B+树高度计算,应该能自然引出分页优化的思考。</think>MySQL索引的核心数据结构是**B+树**,辅以哈希索引等特定场景的优化结构。以下是详细原理分析: --- ### **一、核心数据结构:B+树** #### **1. 结构特性** - **多叉平衡树**:节点可存储多个键值(默认页大小16KB),树高度低(通常3~4层可存百万级数据)。 - **数据全在叶子节点**:非叶子节点仅存**键值+指针**(指向子节点),叶子节点包含**完整数据**(或主键值)。 - **叶子节点双向链表**:所有叶子节点通过指针顺序链接,支持高效范围查询(如`WHERE id BETWEEN 100 AND 200`)。 #### **2. B+树 vs B树** | **特性** | **B+树** | **B树** | |------------------|-----------------------------|----------------------| | 数据存储位置 | 仅叶子节点 | 所有节点均可存储数据 | | 叶子节点链接 | 双向链表 | 无链接 | | 范围查询效率 | $O(\log n + k)$(k为结果数)| $O(\log n \cdot k)$ | | 非叶子节点冗余 | 键值重复存储(加速遍历) | 无冗余 | #### **3. InnoDB实现细节** - **聚簇索引(Clustered Index)**: 表数据按主键顺序存储在B+树叶子节点中,数据行与主键绑定。 例如用户表`users(id PK, name)`,叶子节点结构: ```plaintext | 页1 | → | 页2 | → | 页3 | ...(双向链表) 每页存储:[(id=1, name="Alice"), (id=2, name="Bob"), ...] ``` - **二级索引(Secondary Index)**: 叶子节点存储**主键值**而非数据行物理地址。查询过程: ```sql SELECT * FROM users WHERE name = 'Bob'; ``` 1. 在`name`索引B+树中找到`'Bob'`对应的主键值(如id=2) 2. 用主键值回表查询聚簇索引获取完整数据(*回表操作*) --- ### **二、其他索引类型** #### **1. 哈希索引** - **实现方式**: InnoDB自适应哈希索引(Adaptive Hash Index),由系统自动管理,用户不可控。 - **触发条件**:频繁访问某索引值且模式固定(如`WHERE id = 100`)。 - **数据结构**: 维护一个哈希表,键=索引值,值=数据行位置(非叶子节点指针)。 - **局限性**: 不支持范围查询(如`id > 100`),仅适用于等值查询。 #### **2. 全文索引(FULLTEXT)** - **数据结构**:倒排索引(Inverted Index) 存储`单词 → 文档ID列表`的映射,例如: ``` "database" → [doc1, doc3, doc5] "index" → [doc2, doc4] ``` - **算法**:分词 + BM25/ TF-IDF 相关性排序。 #### **3. 空间索引(R-Tree)** - 用于地理数据(如`GEOMETRY`类型),实现原理为R-Tree(递归分割空间)。 --- ### **三、B+树操作算法** #### **1. 查询过程** - **等值查询**(如`id=100`): 从根节点二分查找,逐层向下,时间复杂度$O(\log_d n)$(d为节点分支因子)。 - **范围查询**(如`id BETWEEN 50 AND 200`): 1. 定位到50的叶子节点 2. 沿链表向后遍历至200 3. 时间复杂度$O(\log_d n + k)$(k为结果数量) #### **2. 插入操作** ```plaintext 1. 定位到目标叶子节点 2. 若节点未满:直接插入有序位置 3. 若节点已满:分裂节点(新节点复制一半数据) - 父节点添加新键值(指向新节点) - 递归检查父节点是否需分裂(可能引发树增高) ``` *示例:插入键值27导致节点分裂* ```mermaid graph TD A[根节点: [20, 40]] --> B[叶子1: 10,20] A --> C[叶子2: 30,40] 插入27 → C[叶子2: 27,30,40] → 分裂为C1[27]C2[30,40] 更新根节点为[20,27,40] ``` #### **3. 删除操作** - 直接删除叶子节点数据 - 若节点元素少于填充因子(默认50%),尝试合并相邻节点 --- ### **四、性能优化机制** 1. **页压缩(Page Compression)** 对索引页应用字典压缩(如`zlib`),减少I/O开销。 2. **Change Buffer** 对非唯一索引的插入/删除操作缓存,延迟写入磁盘(减少随机I/O)。 3. **覆盖索引(Covering Index)** 若查询字段全在索引中,避免回表: ```sql SELECT name FROM users WHERE age > 30; -- 若索引为(age,name)则无需回表 ``` --- ### **五、不同存储引擎对比** | **引擎** | 索引类型 | 数据存储方式 | |----------|------------------|--------------------------| | InnoDB | 聚簇索引+B+树 | 数据行在叶子节点 | | MyISAM | 非聚簇索引+B+树 | 数据与索引分离(.MYD/.MYI)| | Memory | 默认哈希索引 | 堆表 | > **关键结论**:B+树的低高度、顺序访问特性,使其成为磁盘数据库索引的最优解[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值