【MySQL系列笔记】InnoDB引擎-数据存储结构

本文涉及的产品
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: InnoDB 存储引擎是MySQL的默认存储引擎,是事务安全的MySQL存储引擎。该存储引擎是第一个完整ACID事务的MySQL存储引擎,其特点是行锁设计、支持MVCC、支持外键、提供一致性非锁定读,同时被设计用来最有效地利用以及使用内存和 CPU。因此很有必要学习下InnoDB存储引擎,它的很多架构设计思路都可以应用到我们的应用系统设计中。

1. InnoDB 存储引擎

InnoDB 存储引擎是MySQL的默认存储引擎,是事务安全的MySQL存储引擎。该存储引擎是第一个完整ACID事务的MySQL存储引擎,其特点是行锁设计、支持MVCC、支持外键、提供一致性非锁定读,同时被设计用来最有效地利用以及使用内存和 CPU。因此很有必要学习下InnoDB存储引擎,它的很多架构设计思路都可以应用到我们的应用系统设计中。

InnoDB体系架构

我们通过下面这张图先对 InnoDB 存储引擎的体系有一个整体的认识,里面有很多细节后面会分几篇文章来学习。

比如要更新 user 表中 id=1 的这条数据,它的大致流程如下:

  1. 客户端连接到MySQL服务器,将SQL更新语句发送到服务器;MySQL服务器连接池中会有一个连接和客户端建立连接,然后后台线程会从连接中获取到要执行的SQL语句,并发送给SQL接口去调度执行。
  2. 增、删、改 时,会将查询缓存中 user 表相关的缓存都清空。
  3. SQL语句经过SQL解析器解析、优化器优化,得到一个执行路径,前面这些和执行查询其实都是类似的。
  4. 接着由执行引擎去调用底层的存储引擎接口,根据执行计划完成SQL语句的执行。
  • 首先查询出要更新的数据,这一步会先判断缓冲池(Buffer Pool)中是否已经存在这条数据,如果已经存在了,则直接从缓存池获取数据返回。否则从磁盘数据文件中加载这条数据到缓冲池中,再返回数据。
  • 获取到数据后,执行引擎会根据SQL更新数据,然后调用存储引擎更新数据。这一步会对数据加排它锁,避免并发更新问题。之后先写 undolog 到缓冲池,undolog 主要用于事务回滚、MVCC等;同时,undolog 也会产生 redolog 日志。
  • 之后更新缓冲池中的数据,同时记录 redolog 到 RedoLog缓冲池,redolog 主要用于保证数据的持久性,宕机恢复数据等。
  • 最后提交事务,虽然没有手动 commit 提交事务,update 语句执行完成后也会有隐式的事务提交的。事务提交时,会先在MySQL服务器层面会写入 binlog,binlog是数据持久性的保证。最后将redolog刷入磁盘,完成事务提交。
  1. 最底层的一部分就是磁盘上的数据文件、日志文件等,可以看到,InnoDB 设计了缓冲池来缓冲数据、undolog、redolog 等,这些内存中的数据最终都是要刷新到磁盘中才能保证数据不丢失的。

至于为什么要这么设计,我们后面再分析。

2. MySQL数据目录

2.1. 数据目录

我们可以通过 datadir 这个系统变量查看MySQL的数据目录位置,默认是在 /var/lib/mysql 下。

mysql> show variables like 'datadir';
+---------------+-----------------+
| Variable_name | Value           |
+---------------+-----------------+
| datadir       | /var/lib/mysql/ |
+---------------+-----------------+

在一个全新安装的数据库的数据目录下,可以看到如下的一些初始化的文件和目录。

我们可以重点关注 ibdata1、ib_logfile0、ib_logfile1 这几个文件,以后会讲到。

ibdata1 是共享表空间,ib_logfile0、ib_logfile1 是 redo 日志文件。

还有一个 f4e2d8fde38c.pid 的文件,当MySQL实例启动时,会将自己的进程ID写入一个pid文件。该文件可由参数pid_file控制,默认位于数据库目录下,文件名为主机名.pid

mysql> show variables like 'pid_file';
+---------------+---------------------------------+
| Variable_name | Value                           |
+---------------+---------------------------------+
| pid_file      | /var/lib/mysql/f4e2d8fde38c.pid |
+---------------+---------------------------------+

2.2. 数据库目录

MySQL默认创建了四个系统数据库,除了 information_schema,另外三个都会有一个目录与之对应。

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

我们通过 create database xx; 创建一个测试数据库,并指定了字符集为 utf8mb4

mysql> create database test default character set utf8mb4;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+

创建数据库后就会看到多了一个同名的目录,也就是说MySQL中的数据库在文件系统中其实就是数据目录下的一个子目录。

进入数据库目录下可以看到,创建数据库时会同步创建一个名为 db.opt 的文件,这个文件中包含了该数据库的各种属性,比如说该数据库默认的字符集和比较规则等。

2.3. 系统数据库

前边提到了MySQL的几个系统数据库,下面简单看下每个数据库都是干什么的。

  • mysql

这个数据库的核心,它存储了MySQL的用户账户和权限信息,一些存储过程、事件的定义信息,一些运行过程中产生的日志信息,一些帮助信息以及时区信息等。

  • information_schema

这个数据库保存着MySQL服务器所有其他数据库的信息,比如表、视图、触发器、列、索引、锁、事务等等。这些信息并不是真实的用户数据,而是一些描述性信息,也称之为元数据。

  • performance_schema

这个数据库主要保存MySQL服务器运行过程中的一些状态信息,包括统计最近执行了哪些语句,在执行过程的每个阶段都花费了多长时间,内存的使用情况等等信息。

  • sys

这个数据库主要是通过视图的形式把 information_schema 和 performance_schema 结合起来,让程序员可以更方便的了解MySQL服务器的一些性能信息。

2.4. 表结构定义文件

在 test 数据库下,先用下面的SQL创建一张 user 表,指定的存储引擎为 InnoDB:

sql
复制代码CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(60) NOT NULL COMMENT '用户名',
  `nickname` varchar(240) DEFAULT NULL COMMENT '昵称',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_uk_username` (`username`) USING BTREE
) ENGINE=InnoDB;

创建完成之后就可以看到这张表了:

sql
复制代码mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| user           |
+----------------+

这个时候再看 test 目录,会发现多了两个文件:

  • user.frm:表结构定义文件,格式为 表名.frm
  • user.ibd:表空间文件,格式为 表名.ibd

不论表采用哪种存储引擎,每张表都会有一个以.frm为后缀名的文件,这个文件记录了该表的表结构定义。这个.frm文件是以二进制格式存储的,直接打开会乱码。

2.5. 表数据文件

InnoDB将数据按表空间(tablespace)进行存储,MySQL数据目录下名为ibdata1的文件就是默认的表空间文件,也称为共享表空间。可以通过参数innodb_data_file_path对其进行设置,格式如下:

innodb_data_file_path=datafile1[; datafile2]...

可以通过多个文件组成一个表空间,同时制定文件的属性,如:

innodb_data_file_path=/db/ibdata1:2000M;/dr2/db/ibdata2:2000M:autoextend

这里将 /db/ibdata1 和 /dr2/db/ibdata2 两个文件用来组成表空间,同时,两个文件的文件名后都跟了属性,表示文件 idbdata1 的大小为2000MB,文件ibdata2的大小为2000MB,如果用完了这2000MB,该文件可以自动增长(autoextend)。

可以看到默认的 ibdata1 的大小为12M,且支持自动扩展。

mysql> show variables like 'innodb_data_file_path';
+-----------------------+------------------------+
| Variable_name         | Value                  |
+-----------------------+------------------------+
| innodb_data_file_path | ibdata1:12M:autoextend |
+-----------------------+------------------------+

若设置了参数innodb_file_per_table,InnoDB每个表将产生一个独立表空间。独立表空间的命名规则为 表名.ibd,例如前面的 user.ibd。这个配置默认是开启的,就是每张表都有一个独立的表空间文件来存储数据。

mysql> show variables like 'innodb_file_per_table';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| innodb_file_per_table | ON    |
+-----------------------+-------+

3. InnoDB逻辑存储结构

InnoDB将所有数据都存放在表空间中,表空间又由段(segment)、区(extent)、页(page)组成。InnoDB存储引擎的逻辑存储结构大致如下图。

3.1. 表空间

表空间可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。在默认情况下InnoDB存储引擎有一个共享表空间ibdata1,所有数据都存放在这个表空间内。

如果启用了参数innodb_file_per_table,则每张表内的数据可以单独放到一个表空间内。需要注意的是,这些单独的表空间文件仅存储该表的数据、索引和插入缓冲Bitmap等信息,其余信息还是存放在共享表空间中,例如 undo日志、插入缓冲索引页、系统事务信息、二次写缓冲等。

因此即使在启用了参数innodb_file_per_table之后,共享表空间的大小还是会不断地增加,例如事务中写入了undo日志,就算回滚了,共享表空间的大小也不会缩小。但是会判断这些undo信息是否还需要,不需要的话,就会将这些空间标记为可用空间,供下次重复使用。

3.2.

从前面B+树的结构知道,B+树分为叶子节点和非叶子节点,最底层的叶子节点才存储了数据,非叶子节点是索引目录。如果将叶子节点页和非叶子节点页混合在一起存储,那在检索数据的时候同样也会有大量的随机I/O。

所以 InnoDB 又提出了段的概念,常见的段有数据段、索引段、回滚段等。段是一个逻辑上的概念,并不对应表空间中某一个连续的物理区域,它由若干个完整的区组成(还会包含一些碎片页),不同的段不能使用同一个区。

存放叶子节点的区的集合就是数据段,存放非叶子节点的区的集合就是索引段。也就是说一个索引会生成2个段,一个叶子节点段(数据段),一个非叶子节点段(索引段)。

3.3.

在默认情况下,InnoDB存储引擎页的大小为16KB,表空间中的页就太多了。为了更好的管理这些页,InnoDB 将物理位置上连续的64个页划为一个区,任何情况下,每个区的大小都为1MB

B+树中每一层都是通过双向链表连接起来的,如果是以页为单位来分配存储空间,本来链表中相邻的两个页之间的物理位置就可能离得非常远,那么磁盘查询时就会有大量的随机I/O,随机I/O是非常慢的。所以应该尽量让链表中相邻的页的物理位置也相邻,这样可以消除很多的随机I/O,使用顺序I/O,尤其是在进行范围查询的时候。

所以在表中数据量大的时候,为某个索引分配空间的时候就不再按照页为单位分配了,而是按照区为单位分配,甚至在表中的数据非常多的时候,可以一次性分配多个连续的区。

不论是系统表空间还是独立表空间,都可以看成是由若干个区组成的,每个区64个页,然后每256个区又被划分成一组。

第一个组最开始的3个页面的类型是固定的,也就是第一个区(extent0)最开始的三个页。分别是:

  • FSP_HDR:用来登记整个表空间的一些整体属性以及本组所有区的属性,整个表空间只有一个 FSP_HDR 类型的页面。
  • IBUF_BITMAP:存储本组所有区的所有页面关于 INSERT BUFFER 的信息。
  • INODE:索引节点信息。

其余各组则是最开始的2个页面的类型是固定的,分别是:

  • XDES:用来登记本组256个区的属性。
  • IBUF_BITMAP:存储本组所有的区的所有页面关于 INSERT BUFFER 的信息。

从这里也可以看出,索引数据并不时连续存储在区中,因为其中有些页面被用来存储额外的一些管理信息了。

3.4.

页(Page)是 InnoDB 磁盘管理的最小单位,默认每个页的大小为16KB,也就是最多能保证16KB的连续存储空间。

InnoDB 将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,也就是一次最少从磁盘中读取一页16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。

3.5.

InnoDB的数据是按行进行存放的,每个页存放的行记录最多允许存放16KB / 2 -200行的记录,即7992行记录。

每行记录根据不同的行格式、不同的数据类型,会有不同的存储方式。每行除了记录我们保存的数据之外,还可能会记录事务ID(DB_TRX_ID),回滚指针(DB_ROLL_PTR)等。

3.6. 索引组织表

在InnoDB中,行数据都是根据主键顺序存放的,这种存储方式的表称为索引组织表。在InnoDB表中,每张表都有个主键,如果在创建表时没有显式地定义主键,则InnoDB会按如下方式选择或创建主键:

  • 首先判断表中是否有非空的唯一索引,如果有,则该列即为主键。
  • 当表中有多个非空唯一索引时,将选择建表时第一个定义的非空唯一索引为主键。
  • 如果不符合上述条件,InnoDB会自动创建一个名为row_id的6字节的隐藏列作为主键。

为了能快速的从磁盘中检索出数据,InnoDB采用 B+树 结构来组织数据,通过 B+树 组织起来的结构大概就像下图这个样子。(后面索引详细介绍)

可以看到,InnoDB存储引擎表是索引组织的,数据即索引,索引即数据。

相关实践学习
自建数据库迁移到云数据库
本场景将引导您将网站的自建数据库平滑迁移至云数据库RDS。通过使用RDS,您可以获得稳定、可靠和安全的企业级数据库服务,可以更加专注于发展核心业务,无需过多担心数据库的管理和维护。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
6月前
|
存储 关系型数据库 MySQL
介绍MySQL的InnoDB引擎特性
总结而言 , Inno DB 引搞 是 MySQL 中 高 性 能 , 高 可靠 的 存 储选项 , 宽泛 应用于要求强 复杂交易处理场景 。
277 15
|
存储 缓存 关系型数据库
都说InnoDB好,那还要不要使用Memory引擎?
【11月更文挑战第16天】本文介绍了 MySQL 中 InnoDB 和 Memory 两种存储引擎的特点及适用场景。InnoDB 支持事务、外键约束,数据持久性强,适合 OLTP 场景;而 Memory 引擎数据存储于内存,读写速度快但易失,适用于临时数据或缓存。选择时需考虑性能、数据持久性、一致性和完整性需求以及应用场景的临时性和可恢复性。
402 6
|
存储 关系型数据库 MySQL
阿里面试:为什么要索引?什么是MySQL索引?底层结构是什么?
尼恩是一位资深架构师,他在自己的读者交流群中分享了关于MySQL索引的重要知识点。索引是帮助MySQL高效获取数据的数据结构,主要作用包括显著提升查询速度、降低磁盘I/O次数、优化排序与分组操作以及提升复杂查询的性能。MySQL支持多种索引类型,如主键索引、唯一索引、普通索引、全文索引和空间数据索引。索引的底层数据结构主要是B+树,它能够有效支持范围查询和顺序遍历,同时保持高效的插入、删除和查找性能。尼恩还强调了索引的优缺点,并提供了多个面试题及其解答,帮助读者在面试中脱颖而出。相关资料可在公众号【技术自由圈】获取。
|
数据管理 关系型数据库 MySQL
数据管理服务DMS支持MySQL数据库的无锁结构变更
本文介绍了使用Sysbench准备2000万数据并进行全表字段更新的操作。通过DMS的无锁变更功能,可在不锁定表的情况下完成结构修改,避免了传统方法中可能产生的锁等待问题。具体步骤包括:准备数据、提交审批、执行变更及检查表结构,确保变更过程高效且不影响业务运行。
1360 2
|
存储 关系型数据库 MySQL
MySQL引擎InnoDB和MyISAM的区别?
InnoDB是MySQL默认的事务型存储引擎,支持事务、行级锁、MVCC、在线热备份等特性,主索引为聚簇索引,适用于高并发、高可靠性的场景。MyISAM设计简单,支持压缩表、空间索引,但不支持事务和行级锁,适合读多写少、不要求事务的场景。
330 9
|
存储 安全 关系型数据库
InnoDB引擎特性
InnoDB事务型数据库的首选引擎,支持事务安全表(ACID),支持行锁定和外键。MySQL5.5.5之后,InnoDB作为默认存储引擎,InnoDB主要特性有: InnoDB给MySQL提供了具有提交,回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎。InnoDB锁定在行级并且也在SELECT语句中提供了一个类似Oracle的非锁定读。 InnoDB是为处理巨大数据量的最大性能设计。它的CPU效率可能是任何其他基于磁盘关系的数据库引擎所不能匹敌的。 InnoDB存储引擎完全与MySQL服务器整合,InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池
|
搜索推荐 前端开发 数据可视化
基于Python协同过滤的旅游景点推荐系统,采用Django框架,MySQL数据存储,Bootstrap前端,echarts可视化实现
本文介绍了一个基于Python协同过滤算法的旅游景点推荐系统,该系统采用Django框架、MySQL数据库、Bootstrap前端和echarts数据可视化技术,旨在为用户提供个性化的旅游推荐服务,提升用户体验和旅游市场增长。
2020 9
基于Python协同过滤的旅游景点推荐系统,采用Django框架,MySQL数据存储,Bootstrap前端,echarts可视化实现
|
存储 关系型数据库 MySQL
深入解析MySQL数据存储机制:从表结构到物理存储
深入解析MySQL数据存储机制:从表结构到物理存储
1759 1
|
JSON 关系型数据库 MySQL
MySQL JSON数据存储结构与操作
通过本文的介绍,我们了解了MySQL中JSON数据类型的基本操作、常用JSON函数、以及如何通过索引和优化来提高查询性能。JSON数据类型为存储和操作结构化数据提供了灵活性和便利性,在现代数据库应用中具有广泛的应用前景。希望本文对您在MySQL中使用JSON数据类型有所帮助。
1473 0
|
存储 SQL 关系型数据库
(十三)MySQL引擎篇:半道出家的InnoDB为何能替换官方的MyISAM?
MySQL是一款支持拔插式引擎的数据库,在开发过程中你可以根据业务特性,从支持的诸多引擎中选择一款适合的,例如MyISAM、InnoDB、Merge、Memory(HEAP)、BDB(BerkeleyDB)、Example、Federated、Archive、CSV、Blackhole.....
474 2

相关产品

  • 云数据库 RDS MySQL 版
  • 推荐镜像

    更多