上面说的都是MySQL本身的文件,与存储引擎无关。除了这些文件外,每个表存储引擎都还有自己独有的文件:
-
表空间文件
-
重做日志文件
1. 表空间文件
Innodb采用将存储的数据按照表空间(tablespace)进行存放的设计。在默认配置下会有一个初始大小为10MB名为ibdata1的文件。这个文件就是默认的表空间文件(tablespace file),可以通过参数innodb_data_file_path对其进行设置。
查看参数对应值:
mysql> show variables like 'innodb_data_file_path';
+-----------------------+------------------------+
| Variable_name | Value |
+-----------------------+------------------------+
| innodb_data_file_path | ibdata1:12M:autoextend |
+-----------------------+------------------------+
1 row in set (0.01 sec)
用户可以通过用多个文件组成一个表空间,同时制定文件属性。
MySQL表空间的扩容:(当前定义的表空间或默认表空间是不能改变的,否则启动失败,但是可以添加额外的表空间,ibdataN序列根据当前的数量递增)
1. 查看当前数据库系统表空间大小设置:
mysql> show variables like 'innodb_data_file_path';
+-----------------------+-----------------------+
| Variable_name | Value |
+-----------------------+-----------------------+
| innodb_data_file_path | ibdata1:1G:autoextend |
+-----------------------+-----------------------+
1 row in set (0.02 sec)
2. 关闭数据库,修改配置文件:
mysql> shutdown
[root@mysql8 mysql_3306]# vim my_3306.cnf
[root@mysql8 mysql_3306]# cat my_3306.cnf | grep innodb_data_file_path
innodb_data_file_path = ibdata1:1G;ibdata2:200M:autoextend
3. 启动数据库后查看参数值:
mysql> show variables like 'innodb_data_file_path';
+-----------------------+------------------------------------+
| Variable_name | Value |
+-----------------------+------------------------------------+
| innodb_data_file_path | ibdata1:1G;ibdata2:200M:autoextend |
+-----------------------+------------------------------------+
1 row in set (0.00 sec)
4. 查看相应的数据文件目录:
[root@mysql8 data]# ls ibdata*
ibdata1 ibdata2
我这里将数据文件目录下ibdata1,ibdata2用来组成表空间。如果这两个文件在不同磁盘上,磁盘的负载可能会被平均,因此可以提高数据库的整体性能。
ibdata1:1G;ibdata2:200M:autoextend
这两个文件的文件名后面都跟了属性,表示文件ibdata1的大小为2G,文件ibdata2大小为200M,如果这200M用完可以自动扩展。
设置innodb_data_file_path,所有基于Innodb存储引擎的表的数据都会记录到该共享表空间里面。如果设置参数innodb_file_per_table,则用户可以将每个基于Innodb存储引擎的表产生一个独立表空间。
独立表空间的命名规则为:
表名.ibd
。
mysql> show variables like 'innodb_file_per_table';
+-----------------------+-------+
| Variable_name | Value |
+-----------------------+-------+
| innodb_file_per_table | ON |
+-----------------------+-------+
1 row in set (0.01 sec)
mysql> create table test3(id int,name char(20));
Query OK, 0 rows affected (0.11 sec)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IfSf48FH-1604373951421)(https://secure-static.wolai.com/static/jELHUqEQHpxEjRBgiBH1on/image.png)]
因为设置了参数innodb_file_per_table=ON,因此会产生单独的.ibd表空间文件。但是这些单独的表空间文件只会存储该表的数据,索引和插入缓冲等信息,其他信息还是存放在默认的表空间中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KV7XyPJE-1604373951423)(https://secure-static.wolai.com/static/emk9tspzanGg75fd7Eg5yn/image.png)]
2. 重做日志文件
默认情况,在Innodb存储引擎的数据目录下会有名字为ib_logfile0,ib_logfile1…类似的文件。如下所示:
[root@mysql8 data]# ll *logfile*
-rw-r----- 1 mysql mysql 2147483648 Oct 28 16:18 ib_logfile0
-rw-r----- 1 mysql mysql 2147483648 Aug 4 11:04 ib_logfile1
-rw-r----- 1 mysql mysql 2147483648 Aug 4 11:04 ib_logfile2
在MySQL的官方手册中称之为Innodb存储引擎的日志文件,实际上就是重做日志文件(redo log file),这个文件记录了对于Innodb存储引擎的事务日志。
当实例或者介质失败时(media failure)重做日志就会发挥作用,InnoDB存储引擎可以根据重做日志将数据库恢复到异常终止前的时刻,来保证数据的完整性。
每个Innodb存储引擎至少有一个重做日志组(group),每个文件组下至少有两个重做日志文件,ib_logfile0和ib_logfile1就是一个日志组。为了提高可靠性,用户可以设置多个镜像日志组(mirrored log groups),将不同的文件放在不同的磁盘上,以此来提高日志的可用性。
日志组中的每个重做日志文件大小一致,并以循环写入的方式进行,Innodb存储引擎先写日志文件1,当达到文件最后时会切换到日志文件2,当重做日志2写满再切回到重做日志文件1
,其顺序如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hBjSIxcx-1604373951426)(https://secure-static.wolai.com/static/iFPy6fkg7BytLuzc2FxVVP/image.png)]
和重做日志文件有关的参数:
-
innodb_log_file_size
-
innodb_log_files_in_group
-
innodb_mirrored_log_group
-
innodb_log_group_home_dir
参数innodb_log_file_size指定了每个重做日志文件的大小。在Innodb1.2.x版本前,重做日志文件大小不能超过4G,在1.2.x以后将这个限制扩大为512G。
mysql> show variables like 'innodb_log_file_size';
+----------------------+------------+
| Variable_name | Value |
+----------------------+------------+
| innodb_log_file_size | 2147483648 |
+----------------------+------------+
1 row in set (0.01 sec)
参数innodb_log_files_in_group指定了日志文件组中重做日志文件的数量,默认为2。
mysql> show variables like 'innodb_log_files_in_group';
+---------------------------+-------+
| Variable_name | Value |
+---------------------------+-------+
| innodb_log_files_in_group | 3 |
+---------------------------+-------+
1 row in set (0.00 sec)
参数innodb_mirrored_log_groups指定了日志镜像文件组的数量,默认为1,表示只有一个日志文件组,没有镜像。建议生产环境开启镜像文件组,当然如果磁盘本身具有高可用方案,比如磁盘阵列,那么可以不开启。
参数innodb_log_group_home_dir指定了日志文件的所在路径,默认为./,表示存放在MySQL数据库的数据目录下。
下述为MySQL8的重做日志组配置:
mysql> show variables like 'innodb%log%';
+------------------------------------+------------+
| Variable_name | Value |
+------------------------------------+------------+
| innodb_api_enable_binlog | OFF |
| innodb_flush_log_at_timeout | 1 |
| innodb_flush_log_at_trx_commit | 1 |
| innodb_log_buffer_size | 33554432 |
| innodb_log_checksums | ON |
| innodb_log_compressed_pages | ON |
| innodb_log_file_size | 2147483648 |
| innodb_log_files_in_group | 3 |
| innodb_log_group_home_dir | ./ |
| innodb_log_spin_cpu_abs_lwm | 80 |
| innodb_log_spin_cpu_pct_hwm | 50 |
| innodb_log_wait_for_flush_spin_hwm | 400 |
| innodb_log_write_ahead_size | 8192 |
| innodb_max_undo_log_size | 4294967296 |
| innodb_online_alter_log_max_size | 4294967296 |
| innodb_print_ddl_logs | ON |
| innodb_redo_log_encrypt | OFF |
| innodb_undo_log_encrypt | OFF |
| innodb_undo_log_truncate | ON |
+------------------------------------+------------+
19 rows in set (0.01 sec)
重做日志的大小设置对于Innodb存储引擎的性能有着非常大的影响,一方面重做日志的大小不能设置的太大,如果设置比较大,在恢复的时候就需要消耗很长的时间。另一方面又不能设置太小,否则可能导致一个事务的日志需要多次切换重做日志文件。此外,
重做日志文件太小会导致频繁发生async checkpoint
,因而导致性能的抖动。
与二进制日志的区别:
-
二进制日志会记录所有与MySQL数据库相关的日志记录,包括InnoDB、MyISAM、Heap等其他存储引擎的日志。而InnoDB存储引擎的重做日志只记录关于该存储引擎本身的事务日志。
-
除此之外,二者记录的内容也是不同的,无论用户将二进制日志的内容设置成什么格式,其记录都是关于一个事务的具体操作内容,也就是说这个日志是一个逻辑日志。而InnoDB存储引擎的重做日志文件记录的是关于每个页(Page)的更改的物理情况。
-
二者的写入时间不同,二进制日志只在事务提交前进行提交,只会写磁盘一次,不论这个事务有多大。而在事务进行的过程中,却不断有重做日志条目(redo entry)被写到重做日志文件中。
在InnoDB存储引擎中,对于各种不同的操作有着不同重做日志格式。到InnoDB1.2.x版本为止,总共定义了51种重做日志类型。虽然各种重做日志的类型不同,但是他们有着基本的格式如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L0RepebF-1604373951429)(https://secure-static.wolai.com/static/x8vHuSoMGxiPWNddKn2N7q/image.png)]
-
redo_log_type 占用1字节,表示重做日志的类型。
-
space表示表空间ID,但采用压缩的方式,因此占用的空间可能小于4字节
-
page_no 表示页的偏移量,同样采用压缩的方式
-
redo_log_body 表示每个重做日志的数据部分,恢复时需要调用相应的函数进行解析
写入重做日志文件的流程
:
写入重做日志文件的操作不是直接写,而是先写入到日志缓冲(redo log buffer)中,然后按照一定顺序写入到日志文件中。如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oavTf2Ly-1604373951433)(https://secure-static.wolai.com/static/9cMLqbTPUDfPqgou6ZJ4Fm/image.png)]
为什么重做日志写入不需要二次写
从重做日志缓冲往磁盘进行写入的时候,一次写入的大小为512字节,也就是一个扇区的大小。扇区是写入的最小单位,因此可以保证写入是成功的,所以再重做日志的写入过程中不需要有doublewrite的。
关于重做日志缓冲写入到重做日志中的触发条件
1)主线程master thread每秒都会将重做日志缓冲写入到重做日志文件中。
2)参数
innodb_flush_log_at_trx_commit
,该参数表示再提交commit操作的时候,数据库如何处理重做日志的方式。
该参数有效值为:0、1、2
0 代表当前在提交事务的时候,不会将事务的重做日志写入到磁盘上的重做日志文件,而是等待主线程每秒的刷新。
1代表在执行commit操作的时候,将重做日志缓冲同步写到磁盘,即伴有fsync的调用。
2表示将重做日志异步写到磁盘中,即写到文件系统的缓存中。·因此不能保证再执行commit的时候一定伴随写入到重做日志文件中,只是一定会有这个动作发生。
如果要保证事务中的持久性,就一定要将参数
innodb_flush_log_at_trx_commit
设置为1,这样每当有事务提交的时候,都必须保证事务都已经写入到重做日志文件。这样即使数据库意外宕机,也可以通过重做日志文件进行恢复,并且保证可以恢复已经提交的事务。
将重做日志文件设置为0或者2都有可能发生恢复时部分事务的丢失。不同之处在于设置为2时,当MySQL数据库发生宕机而操作系统及服务器并没有发生宕机时,由于此时未写入磁盘的事务日志保存在文件系统缓存中,当恢复时同样能保证数据不丢失。