InnoDB逻辑逻辑存储结构及B树索引

本文详细介绍了MySQL中InnoDB存储引擎的逻辑存储结构,包括表空间、段、区和页的概念,以及如何通过B+树索引来提高查询效率。

github

1 mysql中的逻辑存储结构

1 表空间

基于InnoDB存储引擎的mysql数据库,存放在数据库中的数据实际上是存放在表空间中的。

对于同一个基于InnoDB存储引擎的mysql实例,这个表空间是所有数据库共享的,存放在数据库中各个表中的数据实际会最终存放在表空间中(mysql以ibdata1表示一个共享表空间);但是如果开启了innodb_file_per_table的话,那么mysql数据会为每一个表创建一个私有表空间(以table_name.idb表示),每一个表中的数据会存放在这个私有表空间中,注意此时共享表空间仍然有用,它此时用于存放Undo信息,系统事物信息等等。

2 段

表空间实际上是有若干个段组成的。

3 区

每创建一个表时,mysql都需要向磁盘申请一定的空间用于数据存储,申请的时候就是以区为单位进行申请的,一个区是由连续的64也组成,以页16k,因此一个区的大小是1M。

4 页

页是mysql进行磁盘管理的最小单位,每页的大小是16k;每次我们查询数据库的时候,实际上是mysql先把存放在磁盘中的数据读入到内存中,然后供我们查找,在这个阶段mysql就是以页为单位进行IO操作的。

5 行

基于InnoDB存储引擎的mysql是以行为单位进行数据存储的。

2 数据结构

2.1 二分查找

数据库中使用最最频繁的操作就是查找了,二分查找算法应该算是最普遍且效率较高(有比他更好的,斐波那契但是也是二分查找的变种)的查找算法,但是应用二分查找需要满足两个条件:1)查找序列有序;2)查找序列是一个顺序表;因此我们可以想办法把存放在mysql数据库中的数据按照顺序存放,查找的时候直接使用二分查找,那么可以极大的节省时间。但是数据库并不仅仅只进行查找操作,虽然查找很频繁但是增删改操作同样是mysql数据库必不可少的操作,如果把数据按照顺序表的数据结构进行有序存放,虽然查找效率很高,但是会影响插入和删除的性能(一个长度为n的有序表,删除第i个数据,那么需要移动n-i为数,效率低下)

此时应该想到链表,因为链表中插入数据和删除数据的时间时间复杂度是O(1),但是很遗憾,基于链表(最普通的那种单链表)的有序表并不能进行二分查找。那么能不能既可以使用二分查找,又不用每次进行修改的时候移动太多数据,有!可以使用树。

2.2 二叉排序树

二叉排序树是一种树型数据结构,它要么是一颗空树,要么是一个仅有根节点的树,要么是一个大于两个节点且,节点的左孩子节点值均小于该节点,右孩子节点值均大于该节点。

另外树型结构是可以使用链表来进行表示的,那么理想状态下(二叉排序树是一颗满二叉排序树),那么对这棵树进行查找的话,使用的就是类似于二分查找的方法,效率很高。而且因为使用链表构建的二叉排序树,在进行插入和删除操作的时候,并不需要移动太多的元素,只需要修改指针指向即可,也是可以接受的。

但是这是理想状态下,实际情况,往往不那么理想,如果构建二叉排序树的一组数据本来就是基本有序的话,那么此时构建的二叉排序树实际上和一条链表无异,此时对二叉排序树进行查找实际上就是对一条链表进行查找,时间复杂度为O(n),往往不能接受这种速度。于是,人们就提出了平衡二叉树。

2.3 平衡二叉树

为了抑制二叉排序树的高度过快增长,人们在二叉排序树的基础上提出了平衡二叉树,平衡二叉树首先是一颗二叉排序树,在二叉排序树的基础上,规定一颗节点的左右子树高度之差绝对值不能大于1.这样可以防止二叉排序树在极端情况下恶化成一条链表。使得二叉树的高度在可控范围内,在进行查找的时候,仍然可以以接近二分查找的速度进行,但是在删除和插入操作的时候需要进行额外的左旋右旋等操作,以维持树的平衡因子,但是这个额外的操作所带来的性能损失是可以接受的。

平衡二叉树的应用很普遍,比如说实现实时排序就可以使用平衡二叉树(我认为跳表实际上也可以看做平衡二叉树的变种,因为它们真的很想,当然跳表也可以实现实时排序)。

但是如果是海量数据的话,现在数据库里往往动不动都是百万行以上数据,如果还以平衡二叉树的数据结构形式组织的话,每个节点仅仅存放一个数据,那么再怎么保证平衡,它的高度也是相当客观的,于是人们想出了在一个节点上存放多个数据,于是就有了B树

2.4 B树和B+树

B树又被称之为B-树,每个非叶节点均存放数据,且可以存放不止一个数据(具体定义可以百度),且节点内部数据有序存放,叶子节点不存放数据,如果查找到叶子节点,则认为查找该组数据中没有目标元素;在对基于B树的一组数进行查询操作的时候(非常大的一组数据),从根节点出发,首先把根节点读入到内存,节点内部的数据有序存放,因此在内存中完全可以利用二分查找进行快速定位,如果找到就返回,如果没有找到,再根据节点指针找对应的下一层节点,再次进行相同的操作,如此反复最终找到目标或者返回这组数中没有该元素。

B+树和B树基本上类似,但是不同的是B+树的非叶节点不存放实际数据,而是仅仅作为一个指向最终目标的索引,实际数据都存放在叶子节点中,且叶子节点从左往右业是有序的,但是每个节点里面也是可以存放不止一个元素,且节点内元素有序存放。在进行查找的时候,根据非叶节点,找到最终叶子节点,比较,满足条件则返回数据,否则,该组数据中没有目标元素。

3 B树索引

基于InnoDB存储引擎的mysql数据库支持B树索引,B树索引实际上是B+树数据结构的一种实现。为什么没有用B树数据结构呢?因为B树中的节点中存放的是实际数据,而InnoDB是面向行存储的,一行记录可能非常大,如果将行记录直接存放在节点中的话,一次读入的数据就会减少,因此没有使用B树;使用B+树,节点中仅仅存放索引,并且把一个节点设计成一页,那么一页就能够存放更多的索引,在进行查找的时候,可以减少IO次数。

基于InnoDB的B树索引,索引列按照B+树数据结构顺序存放,最终的叶子节点指向最终记录所在的页所在的叶子节点。在进行查找操作的时候:

  • 把索引的根节点读入到内存中,对节点内部的数据按照二分查找快速定位(1次IO)
  • 读入下一层的对应节点,再次同样快速定位(1次IO)
  • 找到对应也在节点,读入,同样快速定位(n次IO) 这颗B+树有多高就进行多少次IO,资料上说往往都是2-3层

我们试想一下,有一个1000w行记录的数据,每行1k,大概10G大小,每页16k,那么大概需要640页,如果不用索引,我们进行查找的话,就需要遍历,读入第一页…最终找到目标,极端情况下,需要读入640页,也就是说要进行640次IO。

根据主键建立B+树索引,每页可以存放256个索引,那么建立一个三层的B+树即可(256+256^2+256^3>1000w),因此按照索引进行查找的时候,:

  • 把索引根节点所在页读入内存 (1次IO)
  • 读入第二层某个节点页 (2次IO)
  • 找到叶子节点 (3次IO)

比较两者,是不是发现,建立索引之后,查询的效率大大提高了,但是需要主要的是,索引是实实在在存在的,也存放在磁盘中,需要的时候读入即可,如果一张表数据量很少,一次IO就可以把数据页全部读入到内存中,那么此时再建立索引就有点得不偿失了,因为此时需要2次IO。

因此建立索引不要盲目,根据国情出发。

参考:mysql技术内幕:InnoD存储引擎

<think>我们正在讨论InnoDB存储引擎的逻辑结构。根据引用内容,InnoDB逻辑存储结构主要涉及表空间(Tablespace)、段(Segment)、区(Extent)、页(Page)以及行(Row)这几个层次。下面我将逐一详细介绍。 1. **表空间(Tablespace)**: - 表空间是InnoDB存储引擎逻辑结构的最高层,所有的数据都存储在表空间中。 - 表空间可以分为: - **系统表空间(System Tablespace)**:存储InnoDB的数据字典(在MySQL 8.0之前)、双写缓冲区(Doublewrite Buffer)、更改缓冲区(Change Buffer)、undo日志(在MySQL 8.0之前)以及一些表和索引数据(如果表创建在系统表空间中)。系统表空间由一个或多个数据文件组成,由参数`innodb_data_file_path`指定。 - **独立表空间(File-Per-Table Tablespace)**:每个表都有一个独立的表空间文件(.ibd文件),存储该表的索引和数据。这是MySQL 5.6之后的默认行为(由参数`innodb_file_per_table`控制)。 - **通用表空间(General Tablespace)**:多个表可以共享一个通用表空间,通过`CREATE TABLESPACE`创建。 - **Undo表空间(Undo Tablespace)**:在MySQL 8.0中,undo日志从系统表空间中分离出来,存储在单独的undo表空间中,用于存储undo日志。 - **临时表空间(Temporary Tablespace)**:存储临时表的数据。 2. **段(Segment)**: - 表空间由多个段组成,段是分配空间的基本单位。常见的段有数据段、索引段、回滚段等。 - 数据段存储表中的数据(即聚集索引的叶子节点),索引段存储非叶子节点(索引节点),回滚段存储undo日志。 3. **区(Extent)**: - 每个段由多个区组成,区是连续分配的页。在InnoDB中,一个区固定为1MB(在页大小为16KB时,一个区包含64个连续页)。 - 区可以避免频繁的磁盘分配操作,提高性能。 4. **页(Page)**: - 页是InnoDB磁盘管理的最小单位,默认大小为16KB(可以通过参数`innodb_page_size`设置,但一旦创建数据库就不能更改)。 - 页的类型有很多,例如:数据页(B-tree节点)、undo页、系统页、事务数据页、插入缓冲位图页、插入缓冲空闲列表页等。 - 数据页(B-tree节点)存储行记录和索引信息。InnoDB采用B+索引结构,数据页就是B+的叶子节点,索引页是非叶子节点。 5. **行(Row)**: - 页中存储的是行记录。InnoDB存储引擎是面向行的(row-oriented),即数据按行存储。 - 每行记录都包含事务ID(Transaction ID)、回滚指针(Roll Pointer)以及行数据(包括主键和其他列)。 - InnoDB采用索引组织表(Index Organized Table, IOT)的方式存储数据,即表中的数据按照主键顺序存储。如果没有显式定义主键,InnoDB会选择一个唯一非空索引作为主键,如果也没有,则会自动生成一个6字节的rowID作为主键。 总结一下InnoDB逻辑存储结构:表空间由段组成,段由区组成,区由页组成,页中存储行记录。这种层次结构有助于高效地管理磁盘空间和进行数据读写。 另外,根据引用[1]和[3],InnoDB的表是索引组织表,即数据按照主键顺序存储。这种结构使得基于主键的查询非常高效。 [^1]:【MySQLInnodb存储引擎逻辑存储结构与底层实现解析 [^2]:【MySQL数据库】InnoDB存储引擎:逻辑存储结构、内存架构、磁盘架构 [^3]:MySQLInnoDB存储引擎详细介绍 §§相关问题§§ 1. InnoDB存储引擎中,表空间有哪些类型?各自的作用是什么? 2. 为什么InnoDB采用索引组织表(IOT)的方式存储数据?有什么优势? 3. InnoDB的页(Page)默认大小是多少?可以修改吗?修改时需要注意什么? 4. 在InnoDB中,如果没有显式定义主键,会发生什么? 5. InnoDB逻辑存储结构与物理存储结构有什么联系和区别?</think>### InnoDB存储引擎逻辑架构详解 InnoDB存储引擎采用分层的逻辑存储结构,从大到小依次为:**表空间 → 段 → 区 → 页 → 行**。以下是各层级的详细说明: --- #### **1. 表空间(Tablespace)** 表空间是InnoDB逻辑结构的最高层,分为两类: - **系统表空间(System Tablespace)** - 存储内容: - 数据字典(MySQL 8.0前) - 双写缓冲区(Doublewrite Buffer) - 更改缓冲区(Change Buffer) - Undo日志(MySQL 8.0前) - 未指定独立表空间的表数据与索引 - 配置文件:`innodb_data_file_path` 定义数据文件路径和大小[^2][^3]。 - **独立表空间(File-Per-Table Tablespace)** - 每个表单独存储为 `.ibd` 文件(默认启用)。 - 包含表数据、索引及关联的Undo日志(MySQL 8.0+)。 - **通用表空间(General Tablespace)** - 多个表共享一个表空间文件,通过 `CREATE TABLESPACE` 创建。 - **Undo表空间(Undo Tablespace)** - MySQL 8.0+ 将Undo日志从系统表空间分离,独立存储。 --- #### **2. 段(Segment)** 段是表空间的子结构,管理特定类型的数据分配: - **数据段**:存储B+叶子节点(实际行数据)。 - **索引段**:存储B+非叶子节点(索引指针)。 - **回滚段**:存储Undo日志,用于事务回滚。 > 每个索引对应两个段(叶子段+非叶子段),由InnoDB自动管理[^1]。 --- #### **3. 区(Extent)** 区是连续页的集合,用于优化空间分配: - 固定大小:**1MB**(默认页大小16KB时,1区=64页)。 - 作用:减少频繁分配小单元的开销,提升I/O效率。 --- #### **4. 页(Page)** 页是InnoDB磁盘管理的最小单元: - **默认大小**:16KB(可通过 `innodb_page_size` 配置,但需在初始化时设置)。 - 核心类型: - **数据页(B-tree Node)**:存储行记录与索引。 - **Undo页**:存储事务回滚日志。 - **系统页**:管理区、段等元数据。 - 页内结构: - 文件头(File Header):页编号、前后页指针(形成双向链表)。 - 行记录(Row Records):按主键顺序存储(索引组织表)。 - 页目录(Page Directory):槽位加速行记录查找。 --- #### **5. 行(Row)** 行是数据存储的基本单位: - **格式**:支持Compact、Redundant、Dynamic等行格式(默认Dynamic)。 - **组成**: - 事务ID(Transaction ID) - 回滚指针(Roll Pointer) - 列数据(含隐藏主键 `row_id`) - **主键规则**: - 显式定义主键 → 按主键顺序存储(索引组织表)。 - 未定义主键 → 自动生成6字节 `row_id` 作为主键[^1]。 --- ### 逻辑架构总结图 ```plaintext 表空间 (Tablespace) │ ├─ 段 (Segment) → 数据段 / 索引段 / 回滚段 │ │ │ └─ 区 (Extent) → 1MB (64个连续页) │ │ │ └─ 页 (Page) → 16KB (B+节点) │ │ │ └─ 行 (Row) → 事务ID + 列值 ``` > 核心特性:**索引组织表(IOT)** > 所有数据按主键顺序存储在B+中,非主键索引的叶子节点直接指向主键值[^1][^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值