前言
从MySQL 5.5版本开始默认 使用InnoDB作为引擎,它擅长处理事务,具有自动崩溃恢复的特性,在日常开发中使用非常广泛。
下面是官方的InnoDB引擎架构图,主要分为内存结构和磁盘结构两大部分。
上一篇文章,我们解析了一下InnoDB的内存结构,接下来我们解析一下更为复杂的磁盘结构。
一、InnoDB磁盘结构
InnoDB磁盘主要包含 Tablespaces , InnoDB Data Dictionary 、 Doublewrite Buffer 、 redo log 和 Undo Logs 。
- Tablespaces: 表空间分为系统表空间(ibdata1文件)、临时表空间、常规表空间、Undo表空间以及file-per-table表空间。系统表空间又包括双写缓冲区(Doublewrite Buffer)、Change Buffer等
- Doublewrite buffer:innodb 将数据页写到文件之前存放的位置。8.0.20版本之前,doublewrite buffer存放在InnoDB系统表空间中,8.0.20版本后存放在doublewrite中
- Redo log:存储的是log buffer刷到磁盘的数据
- Undo log:存在于global临时表空间中,用于事务的回滚
1. 表空间 ( Tablespaces )
表空间(Tablespaces) : 用于存储表结构和数据 , InnoDB 表空间类型包括系统表空间、 File-Per-Table 表 空间,常规表空间, Undo 表空间,临时表空间等。
- 系统(共享)表空间(The System Tablespace)
- 共享表空间: 包含InnoDB数据字典,Doublewrite Buffer,Change Buffer,Undo Logs的存 储区域。系统表空间也默认包含任何用户在系统表空间创建的表数据和索引数据。
- 物理文件查看
-
系统表空间是一个共享的表空间因为它是被多个表共享的。该空间的数据文件通过参数innodb_data_file_path 控制,默认值是 ibdata1:12M:autoextend (文件名为ibdata1、12MB、自动扩展)。
- 独立表空间(File-Per-Table Tablespaces)
- 默认开启,独立表空间是一个单表表空间,该表创建于自己的数据文件中,而非创建于系统 表空间中。当 innodb_file_per_table 选项开启时,表将被创建于表空间中。否则, innodb将被创建于系统表空间中。
- 每个表文件表空间由一个.ibd数据文件代表,该文件默认被创建于数据库目录中。
- 两种表空间的优缺点
- 共享表空间 优点:可以将表空间分成多个文件存放到各个磁盘上(表空间文件大小不受表大小的限制,如一个表可以分布在不同的文件上)。数据和文件放在一起方便管理。缺点:所有的数据和索引存放到一个文件中,虽然可以把一个大文件分成多个小文件,但是多个表及索引在表空间中混合存储,这样对于一个表做了大量删除操作后表空间中将会有大量的空隙,特别是对于统计分析,日值系统这类应用最不适合用共享表空间。
- 独立表空间优点: 每个表都有自已独立的表空间 , 每个表的数据和索引都会存在自已的表空间中。可以实现单表在不同的数据库中移动 ( 复制 File-per-table 表空间的对应表的数据文件到其他 mysql 数据库实例的表空间下,实现表的导入迁移 ) 。空间可以回收(在独立表空间下,删除或者清空表后,存储空间会立刻返回给操作系统。而在共享表空间下,表空间数据文件的大小不会缩小);缺点: 单表增加过大,如超过 100 个 G 。
- 通用表空间(General Tablespaces)
- MySQL 5.7开始支持通用表空间管理功能,类似于系统表空间,也是共享表空间,可以存储多 个表的数据。
- 通用表空间为通过create tablespace语法创建的共享表空间。通用表空间可以创建于mysql 数据目录外的其他表空间(自定义存储路径),其可以容纳多张表,且其支持所有的行格式。
- 相比File-per-table表空间,通用表空间由于多表共享表空间,消耗的内存会更少一点,具有 潜在的内存优势。(占用的磁盘空间会更小) 。
- 撤销表空间(Undo Tablespaces)
- 撤销表空间,用来保存回滚日志,即undo logs, undo Log 的数据默认在系统表空间ibdata1 文件中;
- 可以通过 innodb_undo_directory 属性 查看回滚表空间的位置。默认路径是mysql的数据存储路径;
- InnoDB使用的undo表空间由 innodb_undo_tablespaces 配置选项控制,设置undo独立 表空间个数,范围为 0-128 , 默认为 0 , 0 表示不开启独立 undo 表空间 , 且 undo 日志存储在ibdata1 文件中。
- undo 日志使用共享表空间存在的问题因为共享表空间不会自动收缩 , 即使事务关闭 ,undo log 也会一直占用空间 , 所以可能会出现因 为大事物而导致 ibdata1 文件过大的问题 .MySQL5.7 中引入了一个新的参数 innodb_undo_log_truncate 表示是否开启自动收缩 undolog 的表空间的操作。如果配置为 ON ,并且配置了 2 个或 2 个以上的 undolog 表空间数据文件,当某一个日志文件大小超过设置的最大值之后,就会自动的收 缩表空间数据文件。
- 8.0 对于undo log存储的进一步优化
从MySQL8.0版本开始,MySQL默认对undo进行了分离操作,也就是说,不需要在初始化手动配置参数,默认会在datadir目录下生成两个undo表空间文件undo_001 和 undo002 并且可以在线的增加和删除undo表空间文件, 进行动态扩容和收缩.
查询undo log信息
- 临时表空间(Temporary Tablespaces)
- 用户创建的临时表和磁盘内部临时表创建于共享临时表空间中。MySQL 5.7起,开始采用独 立的临时表空间,命名ibtmp1文件,初始化12M,且默认无上限。
- 全局临时表空间默认是数据目录的 ibtmp1 文件,所有临时表共享 , 可以通过innodb_temp_data_file_path 属性指定临时表空间的位置。需要注意的是 : 临时表空间最好是设置最大增长限制 , 否则可能会导致 ibtmp1 文件过大 , 占用过多的磁盘空间
- tmp_table_size 参数配置内部内存临时表的大小。
- 如何监控临时表与临时表空间使用情况建议 Created_tmp_disk_tables / Created_tmp_tables 不要超过 25% 。如果Created_tmp_disk_tables 数量很大,查看是否有很多慢 sql ,是否有很多使用临时表的语句。加大 tmp_table_size的值。还可以选择择机重启实例,释放 ibtmp1 文件,和 ibdata1 不同, ibtmp1 重启时会被重新初始化而 ibdata1 则不可以
2. 数据字典 ( Data Dictionary )
- 数据字典(InnoDB Data Dictionary)
MySQL中,数据字典包括了 : 表结构、数据库名或表名、字段的数据类型、视图、索引、表字段信 息、 MySQL 版本信息、存储过程、触发器等内容 . InnoDB 数据字典由内部系统表组成,这些表包含用于查找表、索引和表字段等对象的元数据。元 数据物理上位于 InnoDB 系统表空间中。在 MySQL8.0 之前 由于历史原因,数据字典元数据在一定 程度上与 InnoDB 表元数据文件(.frm文件)中存储的信息重叠。
3. 双写缓冲区 ( Doublewrite Buffer Files)
- 什么是写失效 ( 部分页失效 )
InnoDB的页和操作系统的页大小不一致, InnoDB 页大小一般为 16K ,操作系统页大小为 4K ,
InnoDB 的页写入到磁盘时,一个页需要分 4 次写。
如果存储引擎正在写入页的数据到磁盘时发生了宕机,可能出现页只写了一部分的情况,比如只写 了 4K ,就宕机了,这种情况叫做部分写失效(partial page write),可能会导致数据丢失。
- 双写缓冲区 Doublewrite Buffer
为了解决写失效问题,InnoDB 实现了 double write buffer Files, 它位于系统表空间,是一个存储 区域。
在BufferPool 的 page 页刷新到磁盘真正的位置前,会先将数据存在 Doublewrite 缓冲区。这样在 宕机重启时,如果出现数据页损坏,那么在应用 redo log之前,需要通过该页的副本来还原该页,然后再进行 redo log 重做, double write 实现了 InnoDB 引擎数据页的可靠性 .
默认情况下启用双写缓冲区,如果要禁用Doublewrite 缓冲区,可以将 innodb_doublewrite 设置为0
- 数据双写流程 step1 :当进行缓冲池中的脏页刷新到磁盘的操作时 , 并不会直接写磁盘 , 每次脏页刷新必须要先写 double write .step2 :通过 memcpy 函数将脏页复制到内存中的 double write buffer .step3 : double write buffer 再分两次、每次 1MB, 顺序写入共享表空间的物理磁盘上 , 第一次写 .step4 : 在完成 double write 页的写入后,再将 double wirite buffer 中的页写入各个表的 独立表空间 文件中 ( 数据文件 .ibd), 第二次写 。
- 为什么写两次 ?可能有的同学会有疑问,为啥写两次,刷一次数据文件保存数据不就可以了,为什么还要写共享表空间 ? 其实是因为共享表空间是在 ibdbata 文件中划出 2M 连续的空间,专门给 double write 刷脏页 用的 , 由于在这个过程中, double write 页的存储是连续的,因此写入磁盘为顺序写,性能很高 ; 完成 double write 后,再将脏页写入实际的各个表空间文件,这时写入就是离散的了 .
- Change Buffer系统表空间中的 change buffer 是内存缓冲池中 change buffer 的备份,也就是说被持久化到了系统表空间中。在崩溃恢复的时候会从系统表空间的 change buffer 中读取信息到 buffer pool 。
总结
篇幅原因,本文只解析了InnoDB磁盘结构中的表空间、数据字典、双写缓冲区,对他们的功能、分类等做了细致分析,并对数据的双写流程做了解析。下一篇我们继续把剩下的重做日志、撤销日志、二进制日志做一个全面解析。