MySQL和InnoDB有如下类型文件:
- 参数文件
- 日志文件
- socket文件
- pid文件
- 表结构文件
- 存储引擎文件
下面一一介绍各类型文件。
3.1、参数文件
参数文件名:my.cnf,我们通常更习惯于称之为“配置文件”,Linux默认在/etc/目录下。
MySQL中的参数可以分为两类: 动态(dynamic)参数和 静态(static)参数。
动态参数可以在MySQL实例运行中进行更改。SET命令就是用来更改动态参数的,语法如下:
其中global和session关键字,表明该参数的修改是基于当前会话,还是整个实例的生命周期。SET修改的参数不会写入参数文件,实例重启后会失效。如果需要重启生效,必须修改参数文件。
静态参数说明在整个实例生命周期内都不得进行更改。如果通过SET命令修改静态参数,会报read only的错误。如果需要更改的话,必须修改参数文件,并重启才能生效。
3.2、日志文件
日志文件记录了MySQL运行中的情况,常见的日志文件有:
□ 错误日志(error log):记录错误和警告信息,常用于故障分析。
□ 二进制日志(binary log):较为复杂,下文详细介绍。
□ 慢查询日志(slow query log):记录运行时间超过指定阀值的所有SQL语句,用于定位可能存在问题的SQL语句,从而进行SQL语句层面的优化。mysqldumpslow命令可用于分析该日志。
□ 查询日志(log):查询日志记录了所有对MySQL数据库请求的信息,无论这些请求是否得到了正确的执行。因查询日志写磁盘频繁,对性能有影响,因此生产环境中最好关闭,MySQL默认是关闭的。
这些日志文件有助于我们对数据库的运行状态进行诊断,如故障分析、性能优化等。
二进制日志
二进制日志(binary log),简称binlog,记录了对MySQL数据库执行更改的所有操作,但是不包括SELECT和SHOW这类操作。简单的理解就是,binlog记录了所有写操作。
通过如下命令可以查看binlog:
SHOW
二进制日志主要有以下几种作用:
□ 恢复(recovery):某些数据的恢复需要二进制日志,例如,在一个数据库全备文件恢复后,用户可以通过二进制日志进行point-in-time的恢复。
□ 复制(replication):其原理与恢复类似,通过复制和执行二进制日志使一台远程的MySQL数据库(一般称为slave或standby)与一台MySQL数据库(一般称为master或primary)进行实时同步。
□ 审计(audit):用户可以通过二进制日志中的信息来进行审计,判断是否有对数据库进行注入的攻击。
二进制日志文件在默认没有启动,需要手动启动相应配置项。开启这个选项的对性能有所影响,但是影响十分有限。根据MySQL官方的测试表明,开启二进制日志会使性能下降1%。
二进制日志的配置参数有:
□ max_binlog_size:单个二进制日志文件的最大值,默认1G。如果超过该值,则产生新的二进制日志文件,后缀名+1,并记录到.index文件。
□ binlog_cache_size:二进制日志缓冲区大小,默认32K,是基于会话的。当使用事务时,所有未提交的二进制日志会被记录到这个缓冲区,等该事务提交时,直接将缓冲中的二进制日志写入二进制日志文件。该值设置过大会浪费内存,太小会导致缓冲中的日志写入一个临时文件中,影响性能。可通过SHOW GLOBAL STATUS命令查看binlog_cache_use、binlog_cache_disk_use的状态,来判断当前binlog_cache_size的设置是否合适。
□ sync_binlog:每写缓冲多少次就同步到磁盘。0表示由操作系统控制刷新,性能最好,但可能会丢数据。如果设为1,IO操作频繁,对性能有很大损耗。设为1时,还有另外一个问题。当使用InnoDB时,在一个事务发出COMMIT动作之前,将二进制日志立即写入磁盘。如果已写二进制日志,但是未提交事务,并且此时宕机了,那么在数据库恢复时,由于事务没有COMMIT,会被回滚掉。但是二进制日志不能被回滚,从而导致数据不一致。这个问题可以通过将参数innodb_support_xa设为1来解决。
□ binlog-do-db和binlog-ignore-db:分别表示需要写入或忽略写入哪些库的日志。默认为空,表示需要同步所有库的日志到二进制日志。
□ log-slave-update:如果数据库是slave角色,则它不会将从master取得并执行的二进制日志写入自己的二进制日志文件中去。如果需要写入,要设置该值。
□ binlog_format:参数十分重要,它影响了记录二进制日志的格式。详见下文。
binlog_format配置binlog_format是动态参数,可基于会话设置,也可全局设置。该参数可设的值有STATEMENT、ROW和MIXED。
(1)STATEMENT格式和之前的MySQL版本一样,二进制日志文件记录的是日志的逻辑SQL语句。
(2)在ROW格式下,不再是记录简单的SQL语句,而是记录表的行更改情况。如果设置了binlog_format为ROW,可以将InnoDB的事务隔离基本设为READCOMMITTED,以获得更好的并发性。
(3)在MIXED格式下,MySQL默认采用STATEMENT格式进行二进制日志文件的记录,但是在一些情况下会使用ROW格式,可能的情况有:
a)表的存储引擎为NDB,这时对表的DML操作都会以ROW格式记录。
b)使用了UUID()、USER()、CURRENT_USER()、FOUND_ROWS()、ROW_COUNT()等不确定函数。
c)使用了INSERT DELAY语句。
d)使用了用户定义函数(UDF)。
e)使用了临时表(temporary table)。
存储引擎对二进制日志格式的支持情况:
通常ROW格式可以为数据库的恢复和复制带来更好的可靠性。但也会带来二进制文件大小的增加,有些语句下的ROW格式可能需要更大的容量。比如一条会修改多条记录的UPDATE操作。
在mysql中执行 SHOW BINARY LOGS 命令,即可查看二进制日志文件的列表。
mysqlbinlog 命令,可视化展示出二进制日志中的内容。也可以将内容读取出来,供其他MySQL实用程序使用。
3.3 套接字文件
在UNIX系统下本地连接MySQL可以采用UNIX域套接字方式,这种方式需要一个套接字(socket)文件。套接字文件可由参数socket控制,可通过 SHOW VARIABLES LIKE 'socket' 查看文件路径。默认位于数据库目录下,名为mysql.sock。
3.4 pid文件
当MySQL实例启动时,会将自己的进程ID写入一个文件中——该文件即为pid文件。该文件可由参数pid_file控制,可通过 SHOW VARIABLES LIKE 'pid_file' 查看文件路径,默认位于数据库目录下,文件名为 主机名.pid。
3.5 表结构定义文件
因为MySQL插件式存储引擎的体系结构的关系,MySQL数据的存储是根据表进行的,每个表都会有与之对应的文件。但不论表采用何种存储引擎,MySQL都有一个以frm为后缀名的文件,这个文件记录了该表的表结构定义。
详细了解frm文件格式可查看官方文档:https://dev.mysql.com/doc/internals/en/frm-file-format.html
3.6 InnoDB存储引擎文件
前面介绍的文件都是MySQL的文件,和存储引擎无关。除了这些文件外,每个表存储引擎还有其自己独有的文件。这里具体介绍与InnoDB存储引擎密切相关的文件,包括重做日志文件、表空间文件。
3.6.1 表空间文件
InnoDB采用将存储的数据按表空间(tablespace)进行存放的设计。在默认配置下会有一个初始大小为12MB,名为ibdata1的文件。该文件就是默认的表空间文件(tablespace file),用户可以通过参数innodb_data_file_path配置多个文件组成表空间(注意另外需要配置innodb_data_home_dir=指定home目录,配置为空就根目录,否则会报找不到文件目录的错误),例如:
若这两个文件位于不同的磁盘上,磁盘的负载可能被平均,因此可以提高数据库的整体性能。autoextend表示该文件可以自动增长。
如果设置参数innodb_file_per_table=ON(默认),会产生单独的.ibd独立表空间文件。这些单独的表空间文件仅存储该表的数据、索引和插入缓冲BITMAP等信息,其余信息还是存放在默认的表空间中。
InnoDB表存储引擎文件:
3.6.2 重做日志文件
在默认情况下,在InnoDB存储引擎的数据目录下会有两个名为ib_logfile0和ib_logfile1的文件。在MySQL官方手册中将其称为InnoDB存储引擎的日志文件,不过更准确的定义应该是重做日志文件(redo log file)。它们记录了对于InnoDB存储引擎的事务日志。
当数据库出现故障(如主机断电等),InnoDB会使用重做日志恢复到故障前的状态,以此来保证数据的完整性。
每个InnoDB存储引擎至少有1个重做日志文件组(group),每个文件组下至少有2个重做日志文件,如默认的ib_logfile0和ib_logfile1。为了得到更高的可靠性,用户可以设置多个的镜像日志组(mirrored log groups),将不同的文件组放在不同的磁盘上,以此提高重做日志的高可用性。在日志组中每个重做日志文件的大小一致,并以循环写入的方式运行。先写重做日志文件1,当写满这个文件时,会切换至重做日志文件2,再当重做日志文件2也被写满时,会再切换到重做日志文件1中。
下列参数会影响到重做日志:
□ innodb_log_file_size:指定每个重做日志文件的大小。大小需要适中,如果设置得很大,在恢复时可能需要很长的时间;也不能设置得太小了,否则可能导致一个事务的日志需要多次切换重做日志文件,太小还会导致频繁地发生async checkpoint,导致性能的抖动。
□ innodb_log_files_in_group:指定了日志文件组中重做日志文件的数量,默认为2。
□ innodb_mirrored_log_groups:指定了日志镜像文件组的数量,默认为1,表示只有一个日志文件组,没有镜像。若磁盘本身已经做了高可用的方案,如磁盘阵列,那么可以不开启重做日志镜像的功能。
□ innodb_log_group_home_dir:指定了日志文件组所在路径,默认为./,表示在MySQL数据库的数据目录下。
二进制日志与重做日志的区别:1、二进制日志会记录所有与MySQL数据库有关的日志记录,包括InnoDB、MyISAM、Heap等其他存储引擎的日志。而InnoDB存储引擎的重做日志只记录有关该存储引擎本身的事务日志。
2、记录的内容不同,无论用户将二进制日志文件记录的格式设为STATEMENT还是ROW,又或者是MIXED,其记录的都是关于一个事务的具体操作内容,即该日志是逻辑日志。而InnoDB存储引擎的重做日志文件记录的是关于每个页(Page)的更改的物理情况。
3、写入的时间也不同,二进制日志文件仅在事务提交前进行提交,即只写磁盘一次,不论这时该事务多大。而在事务进行的过程中,却不断有重做日志条目(redo entry)被写入到重做日志文件中。
重做日志有几十种类型,它们有着基本的格式,重做日志条目的结构如下:
□ redo_log_type:占用1字节,表示重做日志的类型
□ space:表示表空间的ID,但采用压缩的方式,因此占用的空间可能小于4字节
□ page_no:表示页的偏移量,同样采用压缩的方式
□ redo_log_body:表示每个重做日志的数据部分,恢复时需要调用相应的函数进行解析
重做日志文件不是直接写文件,而是先写入一个重做日志缓冲中,然后按照一定的条件顺序地写入日志文件。其写入过程如下图:
从重做日志缓冲每次按一个扇区大小写入磁盘,因此可以保证写入必定成功,写入过程也不需要有doublewrite。
重做日志缓冲写入磁盘上的条件:
- 主线程中每秒会将重做日志缓冲写入磁盘的重做日志文件中,不论事务是否已经提交。
- 由参数innodb_flush_log_at_trx_commit控制是否触发写磁盘,表示在提交(commit)操作时,处理重做日志的方式。
参数innodb_flush_log_at_trx_commit的配置如下:
0:代表当提交事务时,并不将事务的重做日志写入磁盘上的日志文件,而是等待主线程每秒的刷新。
1:表示在执行commit时将重做日志缓冲同步写到磁盘,即伴有fsync的调用。为了保证事务的ACID中的持久性,必须设置为1。
2:表示将重做日志异步写到磁盘,即写到文件系统的缓存中。因此不能完全保证在执行commit时肯定会写入重做日志文件,只是有这个动作发生。
参考
- 《MySQL技术内幕:InnoDB存储引擎(第2版)》
- 源码:MariaDB 10.4.12