Innodb存储引擎-文件(参数文件.cnf、日志文件、套接字文件.sock、pid文件.pid、表结构定义文件.frm、表空间文件ibdata1、重做日志文件)

文件

主要介绍一些Mysql数据库和InnoDB存储引擎表的各种类型文件,主要有:

  • 参数文件:告诉MySQL实例启动时在哪里可以找到数据库文件,并且指定某些初始化参数,这些参数定义了某种内存结构的大小等设置,还会介绍各种参数的类型。
  • 日志文件:用来记录MySQL实例对某种条件做出响应时写人的文件,如错误日志文件二进制日志文件慢查询日志文件查询日志文件等。
  • socket文件:当用UNIX域套接字方式进行连接时需要的文件。
  • pid文件:MySQL实例的进程ID文件。
  • MySQL表结构文件:用来存放MySQL表结构定义文件。
  • 存储引擎文件:因为MySQL表存储引擎的关系,每个存储引擎都会有自己的文件来保存各种数据。这些存储引擎真正存储了记录和索引等数据。

参数文件(.cnf)

启动实例时,MySQL数据库会去读取配置文件,根据配置文件的参数来启动数据库实例(寻找数据库的各种文件所在位置以及制定某些初始化参数)。在默认情况下,MySQL实例会按照一定的顺序在指定的位置进行参数文件的读取,用户只需通过下面的命令来进行查看即可:

> mysql --help | grep my.cnf
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf  ~/.my.cnf

可以看到,数据库按/etc/my.cnf =》/etc/mysql/my.cnf =》 /usr/local/mysql/etc/my.cnf =》 ~/.my.cnf 的顺序读取配置文件的。如果几个配置文件中都有同一参数,则以最后读取到的配置文件为准

这与Oracle的参数文件(spfile) 相似,不同的是,Oracle 中如果没有参数文件,在启动实例时会提示找不到该参数文件,数据库启动失败。而在MySQL数据库中,可以没有配置文件,在这种情况下,MySQL会按照编译时的默认参数设置启动实例

但是,如果MySQL实例在默认的数据库目录下找不到mysql架构,则启动同样会失败,此时可能在错误日志文件中找到如下内容:

MySQL的mysql架构中记录了访问该实例的权限,当找不到这个架构时,MySQL实例不会成功启动。

(1)什么是参数

数据库参数可以看成K/V键值对,比如:inoodb_buffer_pool_size=1G,那么“inoodb_buffer_pool_size”就是键,“1G”就是值。可以通过SHOW VARIABLES查看数据库的所有参数,也可以通过LIKE来过滤,

5.1版本开始,还可以通过information_schema架构下的GLOBAL_VARIABLES试图来查找。

select * from information_schema.global_variables where variable_name like 'innodb_buffer_pool_size';

Oracle数据库存在所谓的隐藏参数,以供Oracle“内部人士”使用,SQL Server也有类似的参数。但是MySQL没有这类参数。当然,也不需要隐藏参数,而且在大多数情况下,这些数据库厂商也不建议用户在生产环境中对其进行很大的调整。

(2)参数类型

MySQL数据库中的参数分为两类:

  • 动态(dynamic)参数:动态参数意味着可以在MySQL实例运行中进行更改
  • 静态(static)参数:静态参数在整个实例声明周期内都不得进行更改(只读)

可以通过 SET命令对动态的参数值进行修改,SET的语法如下:

	SET
	| [global | session ] system_var_name= expr
	| [@@global. l @@session. l @@]system_var_name= expr

这里可以看到global和session关键字,它们表示该参数的修改是基于当前会话还是整个实例的生命周期。

  • 有些参数值只可以在当前会话中生效,如autocommit
  • 有些参数可以在整个实例声明周期中生效,如binlog_cache_size
  • 有些参数既可以在会话中也可以在整个实例的生命周期内生效,如read_buffer_size

演示案例:例如下面使用set命令修改当前会话的“read_buffer_size”参数值,可以看到当前会话的参数修改了,但是全局参数依然没有变化。

用户也可以通过“set @@global|@@session”来修改当前会话/整个实例的参数。例如下面演示与上面类似的案例:

注意:set命令修改的参数仅在本次MySQL服务中有效,当MySQL重启之后,参数值恢复为原先的模样。如果想永久的改变参数值,应该去修改MySQL的配置文件。

日志文件

日志文件用来记录 MySQL 实例对某种条件做出响应时写入的文件,如错误日志文件、二进制日志文件、慢查询日志文件、查询日志文件等。

Mysql最常见的日志有:

  1. 错误日志文件(error log)
  2. 二进制文件(binlog)
  3. 慢查询文件(slow query log)
  4. 查询日志(log)
错误日志文件(error log)

错误日志文件对MySQL的启动、运行、关闭过程进行了记录。遇到问题时应该首先查看该文件以便定位问题。该文件不仅记录了所有的错误信息,也记录一些警告信息或正确的信息。用户可以通过下面命令来查看错误日志文件的位置:

show variables like 'log_error'\G;

  • 如果默认错误日志路径是console,则服务器将log_error系统变量设置为stderr
  • 否则,默认目标是文件,服务器将设置log_error为文件名,比如:/mysql_data_2/stargazer.log,在默认情况下错误文件的文件名为服务器的主机名,该主机名为stargazer,所以错误文件名为startgazererr。

当出现MySQL数据库不能正常启动时,第一个必须查找的文件应该就是错误日志文件,该文件记录了错误信息,能很好地指导用户发现问题。当数据库不能重启时,通过查错误日志文件可以得到如下内容:

这里,错误日志文件提示了找不到权限库mysql,所以启动失败。有时用户可以直接在错误日志文件中得到优化的帮助,因为有些警告(warning) 很好地说明了问题所在。而这时可以不需要通过查看数据库状态来得知,例如,下面的错误文件中的信息可能告诉用户需要增大InnoDB存储引擎的redo log:

慢查询日志(slow query log)

(1)基本使用

慢查询日志(slow log)可帮助 DBA 定位可能存在问题的 SQL 语句,从而进行 SQL 语句层面的优化。例如,可以在 MySQL 启动时设一个阈值,将运行时间超过该值的所有 SOL 语句都记录到慢查询日志文件中。DBA 每天或每过一段时间对其进行检查,确认是否有 SOL语句需要进行优化。该阈值可以通过参数 long_query_time 来设置,默认值为 10,代表 10秒。

在默认情况下,MySQL 数据库并不启动慢查询日志,用户需要手工将这个参数设为ON:

这里有两点需要注意:

  • 首先,设置 long_query_time 这个阈值后,MySQL 数据库会记录运行时间超过该值的所有 SQL 语句,但运行时间正好等于 long_query_time 的情况并不会被记录下。也就是说,在源代码中判断的是大于long_query_time,而非大于等于。
  • 其次,从 MySQL 5.1 开始,long_query_time 开始以微秒记录 SQL 语句运行的时间,之前仅用秒为单位记录。

另一个和慢查询日志有关的参数是 log_queries_not_using_indexes如果运行的 SQL语句没有使用索引,则 MySQL 数据库同样会将这条 SQL 语句记录到慢查询日志文件。首先确认打开了log_queries_not_using_indexes:

MySQL 5.6.5 版本开始新增了一个参数 log_throttle_queries_not_using_indexes(throttle:风门, 喉咙),用来表示每分钟允许记录到 slow log 的且未使用索引的 SQL 语句次数。该值默认为 0,表示没有限制。在生产环境下,若没有使用索引,此类 SOL 语句会频繁地被记录到 slow log,从而导致 slow log 文件的大小不断增加,故 DBA 可通过此参数进行配置。

DBA可以通过慢查询日志来找出有问题的SQL语句,对其进行优化。然而随着MySQL数据库服务器运行时间的增加,可能会有越来越多的SQL查询被记录到了慢查询日志文件中,此时要分析该文件就显得不是那么简单和直观的了。而这时MySQL数据库提供的mysqldumpslow命令,可以很好地帮助DBA解决该问题:

例如用户希望在david.log慢查询日志文件中得到执行时间最长的10条SQL语句,可以运行如下的命令:

mysqldump -s -al -n 10 david.log

(2)slow_log表

MySQL 5.1 开始可以将慢查询的日志记录放入一张表中,这使得用户的查询更加方便和直观。慢查询表在 mysq架构下,名为 slow_log,其表结构定义如下∶

参数 log output 指定了慢查询输出的格式,默认为 FILE,可以将它设为 TABLE,然后就可以查询 mysql 架构下的 slow_log 表了,如:

参数 log_output是动态的,并且是全局的,因此用户可以在线进行修改。

查看 slow_log 表的定义会发现该表使用的是 CSV 引擎,对大数据量下的查询效率可能不高。用户可以把 slow_log 表的引擎转换到 MyISAM,并在 start_time 列上添加索引以进一步提高查询的效率。但是,如果已经启动了慢查询,将会提示错误∶

不能忽视的是,将 slow_log 表的存储引擎更改为 MyISAM后,还是会对数据库造成额外的开销。不过好在很多关于慢查询的参数都是动态的,用户可以方便地在线进行设置或修改。

(3)读取捕获和long_query_io

InnoSQL版本加强了对于SQL语句的捕获方式。在原版MySQL的基础上在慢查询日志中增加了对于逻辑读取(logical reads)和物理读取(physical reads)的统计:

  • 物理读取:是指从磁盘进行IO读取的次数
  • 逻辑读取:包含所有的读取,不管是磁盘还是缓冲池

例如,下面的例子可以看到该子查询的逻辑读取次数是91584次,物理读取为19次。

从逻辑读与物理读的比例来看,该SQL语句可进行优化。

用户可以通过额外的参数 long_query_io 将超过指定逻辑 IO 次数的 SQL 语句记录到 slow log 中。该值默认为 100,即表示对于逻辑读取次数大于100 的 SQL 语句,记录到 slow log 中。为了兼容原MySQL 数据库的运行方式,还添加了参数 slow_query_type,用来表示启用 slow log 的方式,可选值为∶

  • 0:表示不将 SQL 语句记录到 slow log
  • 1:表示根据运行时间将 SQL 语句记录到 slow log
  • 2:表示根据逻辑 IO 次数将 SQL 语句记录到 slow log
  • 3:表示根据运行时间及逻辑 IO 次数将 SQL 语句记录到 slow log
查询日志(query log)

查询日志记录了所有对MySQL数据库请求的信息,无论这些请求是否得到了正确的执行。默认文件名:主机名.log

通过上述查询日志会发现,查询日志甚至记录了对Access denied的请求,即对于未能正确执行的SQL语句,查询日志也会进行记录。

同样的,从MySQL 5.1开始,可以将查询日志的记录放入mysql架构下的general_log表,该表的使用方法和上面提到的slow_log表的使用方式一样(见上)。

二进制日志(bin log)

MySQL 整体来看,其实就有两块:

  • 一块是 Server 层,它主要做的是 MySQL 功能层面的事情;
  • 还有一块是引擎层,负责存储相关的具体事宜。

redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)。

二进制日志(binary log)是 MySQL 的逻辑日志,记录了对MySQL数据库执行更改的所有操作,若操作本身没有导致数据库发生变化,该操作可能也会写入二进制文件,用任何存储引擎的 MySQL 数据库都会记录 binlog 日志。但是不包括select和show这类操作(因为这些操作对数据本身不会进行修改)。

从上述例子中可以看到,MySQL数据库首先进行UPDATE操作,从返回的结果看到Changed为0,这意味着该操作并没有导致数据库的变化。但是通过命令SHOW BINLOG EVENT可以看出在二进制日志中的确进行了记录。

如果用户想记录SELECT和SHOW操作,那只能使用查询日志,而不是二进制日志。

此外,二进制日志还包括了执行数据库更改操作的时间等其他额外信息。总的来说,二进制日志主要有以下几种作用:

  • 恢复(recovery):某些数据的恢复需要二进制日志,例如,在一个数据库全备文件恢复后,用户可以通过二进制文件进行point-in-time的恢复
  • 复制(replication):其原理与恢复类似,通过复制和执行二进制日志使一台远程的MySQL数据库(一般称为slave或standby)与一台MySQL数据库(一般称为master或primary)进行实时同步
  • 审计(audit):用户可以通过二进制日志中的信息来进行审计,判断是否有对数据库进行注入的攻击

通过配置参数log-bin [=name]可以启动二进制日志。如果不指定name,则默认二进制日志文件名为主机名,后缀名为二进制日志的序列号,所在路径为数据库所在目录(datadir),如:

  • bin_log.00001即为二进制日志文件,使我们在配置文件中指定的名称,所以没有偶使用默认的文件名
  • bin_log.index为二进制的索引文件,用来存储过往产生的二进制日志序号,通常情况下,不建议手动修改这个文件

二进制日志文件在默认情况下并没有启动,需要手动指定参数来启动。开启这个选项会对MySQL的性能造成影响,但是性能损失十分有限。根据MySQL官方手册中的测试指明,开启二进制日志会使性能下降1%。但考虑到可以使用复制和point-in-time的恢复,这些性能损失是可以被接受的。

以下配置文件的参数影响着二进制日志记录的信息和行为:

  • max_binlog_size:单个二进制日志文件的最大值
  • binlog_cache_size:二进制日志的缓冲大小
  • sync_binlog:同步磁盘策略
  • binlog-do-db:需要哪些写入日志的库
  • binlog-ignore-db:忽略哪些写入日志的库
  • log-slave-update:slave取得并执行master的二进制日志后,会写入到自己的二进制日志
  • binlog_format:选择日志的格式:statement、row、mixed

(1)max_binlog_size

该参数指定了单个二进制日志文件的最大值,如果超过该值,则产生新的二进制日志文件,后缀名+1,并记录到.index文件,从MySQL 5.0开始的默认值为1073741824,代表1G(在之前版本中的默认值为1.1G)

show variables like 'max_binlog_size'\G;

(2)binlog_cache_size

当使用事务的表存储引擎(如InnoDB存储引擎)时,所有未提交的二进制日志会被记录到一个缓冲中去,等待事务提交时直接将缓冲中的二进制日志写入二进制日志文件,该缓冲的大小由该参数决定。默认值为32K

此参数是基于会话的,因此该值的设置需要小心,不能设置过大。当一个事务的记录大于设定的binlog_cache_size时,MySQL会把缓冲中的日志写入一个临时文件中,因此该值又不能设置的太小通过show global status可以查看binlog_cache_usebinlog_cache_disk_use的状态,判断当前binlog_cache_size的设置是否适合与当前会话,如果binlog_cache_disk_use次数过多,需要调整binlog_cache_size的值:

  • binlog_cache_use:记录了使用缓冲写二进制日志的次数
  • binlog_cache_disk_use:记录了使用临时文件写二进制日志的次数

(3)sync_binlog

在默认的情况下,二进制日志并不是在每次写的时候同步到磁盘因此,当数据库所在操作系统发生宕机时,可能会有最后一部分数据没有写入二进制日志文件,这会给恢复和复制带来问题。这个参数是对于MySQL系统来说是至关重要的,他不仅影响到Binlog对MySQL所带来的性能损耗,而且还影响到MySQL中数据的完整性。sync_binlog = [N]表示每写缓冲多少次就同步到磁盘,参数取值如下:

  • 0(默认值):当事务提交之后,MySQL不做fsync之类的磁盘同步指令刷新binlog_cache中的信息到磁盘,而让File system自行决定什么时候来做同步,或者cache满了之后才同步到磁盘,性能最佳
  • 1:当每进行1次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘,是最慢的,但是最安全
  • N:当每进行n次事务提交之后,MySQL将进行一次fsync之类的磁盘同步指令来将binlog_cache中的数据强制写入磁盘。

大多数情况下,对数据的一致性并没有很严格的要求,所以并不会把 sync_binlog 配置成 1。为了追求高并发,提升性能,可以设置为 100 或直接用 0。而和 innodb_flush_log_at_trx_commit 一样,对于支付服务这样的应用,还是比较推荐 sync_binlog = 1。

(4)binlog-do-db、binlog-ignore-db

这两个参数分别表示需要写入或忽略写入哪些库的日志默认为都为空,表示需要同步所有库的日志到二进制日志。如果需要设置多个数据库,可以将库名用逗号隔开,或者按照格式每个一行。

(5)log-slave-update

如果当前数据库是复制中的slave角色,则它不会将从master取得并执行的二进制日志写入自己的二进制日志文件中去。如果需要写入,需要设置log-slave-update
如果需要搭建master->slave->slave架构的复制,则必须设置该参数。

(6)binlog_format

该参数影响了记录二进制日志的格式。在MySQL 5.1版本之前没有这个参数,MySQL 5.1开始引入了这个参数。没有这个参数之前,所有二进制文件的格式都是基于SQL语句(statement)级别的,因此基于这个格式的二进制日志文件的复制和Oracle的逻辑Standby有点相似。

该参数对于复制有一定的影响:

  • 如在主服务器运行rand、uuid等不确定函数,又或者使用触发器、用户定义函数等操作,这些都可能导致主从服务器上表中数据的不一致
  • 会发现InnoDB的默认事务隔离级别是REPEATABLE READ。这其实也是因为二进制日志文件格式的关系,如果使用READ COMMITTED的事务隔离级别,会出现类似丢失更新的现象,从而出现主从数据库上的数据不一致

binlog 日志有三种格式,分别为:statement、row 和 minxed:

  • statement基于SQL语句的复制(statement-based replication, SBR),也可以理解为 SQL 语句的原文;

  • row基于行记录的复制(row-based replication,RBR),不记录sql语句上下文相关信息,而是记录哪条记录被修改的细节,binlog 里面记录的是 event(Table_map,Write_rows,Delete_rows);

    对statement格式下复制的问题予以解决:①不用担心rand、uuid等不确定函数、触发器和用户定义函数等操作,导致主从服务器上表数据的不一致;②如果设置了binlog_format为ROW,可以将InnoDB的事务隔离基本设为READ COMMITED,以获得更好的并发性。

    但它的缺点是:会记录每行的修改,因此会带来二进制文件大小的增加,而且由于复制是采用传输二进制日志方式实现的,因此复制的网络开销也会变大。

  • mixed:基于 statement 和 row 两种模式的混合复制(mixed-based replication, MBR),一般情况下使用statement格式来进行保存,当遇到statement无法解决时,切换为row格式来进行保存。可能的情况是:

    • 表的存储引擎为NDB,这时对表的DML操作都会以ROW格式记录
    • 使用了UUID()、USER()、CURRENT_USER()、FOUND_ROWS()、ROW_COUNT()等不确定函数
    • 使用了INSERT DELAY语句
    • 使用了用户定义函数(UDF)
    • 使用了临时表

写入机制:

  1. 根据记录模式和操作触发event事件生成log event(事件触发执行机制)

  2. 将事务执行过程中产生log event写入缓冲区,每个事务线程都有一个缓冲区 Log Event保存在一个binlog_cache_mngr数据结构中,在该结构中有两个缓冲区,一个是 stmt_cache,用于存放不支持事务的信息;另一个是trx_cache,用于存放支持事务的信息。

  3. 事务在提交阶段会将产生的log event写入到外部binlog文件中。 不同事务以串行方式将log event写入binlog文件中,所以一个事务包含的log event信息在 binlog文件中是连续的,中间不会插入其他事务的log event。

binlog_format参数还有对于存储引擎的限制,如下表所示:

binlog_format**参数是动态的,**因此可以在运行时进行修改。

在通常情况下,我们将参数 binlog_format 设置为 ROW,这可以为数据库的恢复和复制带来更好的可靠性。但是不能忽略的一点是,这会带来二进制文件大小的增加。

比如:我们有两张一样的表,大小都为100w,分别执行UPDATE操作,观察二进制日志大小的变化。

  • 当前会话的binlog_format格式为STATEMENT,当我们更改数据后,二进制日志大小只增加了200字节(306-106)

  • 我们把当前会话的binlog_format设置为ROW格式,同时对t2表进行了更新操作,可以看到在ROW格式下竟然需要13782094字节,二进制日志大小差不多增加了13MB,而且执行时间也有所增加(这里设置了sync_binlog=1)。因为这时MySQL不再将逻辑的SQL操作记录到二进制日志中,而是记录对于每行的更改。

由此看到,使用row格式会对磁盘空间要求有一定增加,而且由于复制是采用传输二进制日志方式实现的,因此复制的网络开销也会变大。

二进制日志文件采用的是二进制方式存储,因此不容易进行查看。MySQL提供了mysqlbinlog工具用来对二进制日志文件进行查看。

(1)对STATEMENT格式的二进制日志文件查看,会显示出执行的逻辑SQL语句,如下所示:

(2)但是如果对ROW格式的记录进行查看,会使得结果变为“非常难读”,如下所示:

因此为了方便查看ROW格式的,需要加上参数-v或-vbv即可看到执行的具体信息(-vv会比-v多显示出更新的类型),如下所示:

通过查看我们知道依据简单的update操作记录了对于整个行更改的信息,这也解释了上面的演示案例为什么更新了10W行的数据,在ROW格式下,二进制日志文件会增大13MB。

套接字文件(.sock)

在前面介绍MySQL通信方式时介绍过套接字文件,在UNIX系统下本地连接MySQL可以采用UNIX域套接字方式,这种方式需要一个套接字文件。套接字文件可由参数socket控制,如下所示:

show variables like 'socket'\G;

pid文件(.pid)

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

show variables like 'pid_file'\G;

表结构定义文件(.frm)

因为MySQL插件式存储引擎的体系结构的关系,MySQL数据的存储是根据表进行的,每个表都会有与之对应的文件。但不论采用何种存储引擎,MySQL都有一个以frm为后缀名的文件,这个文件记录了该表的表结构定义

frm还用来存放视图的定义,如用户创建一个v_a视图,那么对应的会产生一个v_a.frm文件,用来记录视图的定义,该文件时文本文件,可以直接使用cat命令进行查看:

InnoDB存储引擎文件

前面介绍的文件都是MySQL数据库本身的文件,和存储引擎无关。接下来介绍与存储引擎有关的独有的文件。本文将介绍与InnoDB密切相关的文件,这些文件包括重做日志文件、表空间文件

表空间文件(ibdata1)

InnoDB采用将存储的数据按表空间(tablespace)进行存放的设计。在默认配置下会有一个初始大小为10MB,名为idbata1的文件(见下图),该文件就是默认的表空间文件。用户可以通过参数innodb_data_file_path对其进行设置:

格式如下:

innodb_data_file_path=datafile_spec1[;datafile_spec2]...

如果用户想通过多个文件组成一个表空间,并且制定文件的属性,则可以使用如下的案例格式:

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

这里把/db/ibdata1和/dr2/db/ibdata2两个文件用来组成表空间。若这两个文件位于不同的磁盘上,磁盘的负载可能被平均,因此可以提高数据库的整体性能。同时文件名后面还跟了文件大小属性:表示ibdata1的大小为2000MB,ibdata2的大小为2000MB,如果用完了这2000MB,该文件会自动增长。

设置 innodb_data_file_path参数后,所有基于InnoDB 存储引擎的表的数据都会记录到该共享表空间中。若设置了参数 innodb_file_per_table,则用户可以将每个基于 InnoDB 存储引擎的表产生一个独立表空间。独立表空间的命名规则为∶ 表名 .ibd。通过这样的方式,可以将表的数据、索引和插入缓冲BITMAP等信息存入单独的表空间,而不用将所有数据都存放于默认的表空间中

表Profile、t1、t2都是基于InnoDB存储的表,由于设置了参数innodb_file_per_table=ON,因此产生了独立的.ibd独立表空间文件。但需要注意的是:这些单独的表空间文件仅存储表的数据、索引和插入缓冲BITMAP等信息,其余信息还是存放到默认的表空间。

重做日志文件(redo log)
核心

redo log 包括两部分:

  1. 内存中的日志缓冲(redo log buffer),该部分日志是易失性的;包含一个或多个log block,大小为512B。
  2. 磁盘上的重做日志文件(redo log file),该部分日志是持久的。

redo log是物理日志,记录事务对数据页做了哪些修改。MySQL 每执行一条 DML 语句,先将记录写入 redo log buffer,后续根据写入策略将多个log block写到 redo log file。策略有:

  • 0 :设置为 0 的时候,表示每次事务提交时不进行刷盘操作,每次提交事务都只把redo log留在redo log buffer中
  • 1(默认值) :设置为 1 的时候,表示每次事务提交时都将进行刷盘操作(默认值),每次提交事务都将redo log 持久化到磁盘上,也就是write+fsync
  • 2 :设置为 2 的时候,表示每次事务提交时都只把 redo log buffer 内容写入 page cache ,也就是只write,不fsync,因此不能完全保证在执行commit时肯定会写入redo log file中。

innodb_flush_log_at_trx_commit 参数默认为 1 ,也就是说当事务提交时会调用 fsync 对 redo log 进行刷盘。另外,InnoDB 存储引擎有一个后台线程,每隔1 秒,就会把 redo log buffer 中的内容写到文件系统缓存(page cache),然后调用 fsync 刷盘。(一个没有提交事务的 redo log 记录,也可能会刷盘

因此为了保证实物的ACID中的持久性,必须将innodb_flush_log_trx_commit设置为1,也就是每当有事务提交时,就必须确保事务都已经写入重做日志文件。那么当数据库因为意外发生宕机时,可以通过重做日志文件恢复,并保证可以恢复已经提交的事务。而将重做日志文件设置为0或2,都有可能发生恢复时部分事务的丢失。不同之处在于,设置为2时,当MySQL数据库发生宕机而操作系统及服务器并没有发生宕机时,由于此时未写入磁盘的事务日志保存在文件系统缓存中,当恢复时同样能保证数据不丢失。

这种先写先写 redo log buffer,再写 redo log file 的技术就是 MySQL 里经常说到的 WAL(Write-Ahead Logging) 技术。存在的意义主要就是降低对数据页刷盘的要求。redo log记录了数据页上的修改,但是当数据页也刷回到磁盘后,这些记录就失去作用了。

InnoDB中至少有一个redo group,每个redo group至少有两个redo log file(ib_logfile0,ib_logfile1),每个redo log file大小固定,除了保存4个512B块:log file header,checkpoint1,空,checkpoint2,其它用于保存log block(512B)。采用循环写入的方式,先写redo log file1,写满后再写redo log file2,然后再写redo log file1这样。

两个CP是因为交替写入,避免因介质失败而导致无法找到可用的CP的情况。

为了得到更高的可靠性,用户可以设置多个日志镜像文件组,将不同的文件组放在不同的磁盘上,以此来提高重做日志高可用性。

重做日志块除了日志本身之外,还由 日志块头(log block header) 及 日志块尾(logblock tailer) 两部分组成。重做日志头一共占用12 字节,重做日志尾占用8 字节。故每个重做日志块实际可以存储的大小为 492 字节(512-12-8) ,如图:

  • log buffer 是由 log block 组成,在内部 log buffer 就好似一个 数组,因此 LOG_BLOCK_HDR_NO 用来标记这个数组中的位置。其是 递增并且循环使用 的,占用 4个字节,但是由于第一位用来判断是否是flush bit, 所以最大的值为 2G (4字节,32位,减去1位,也就是31位);
  • LOG_ BLOCK_ HOR_ DATA _LEN 占用2 字节,表示log block 所占用的大小。当logblock 被写满时,该值为Ox200, 表示使用全部log block 空间,即占用512 字节;
  • LOG_BLOCK_FIRST_REC_GROUP占用2 个字节,表示 log block 中第一个日志所在的偏移量。如果该值的大小和LOG_BLOCK_OR_DATA_LEN 相同,则表示当前log block 不包含新的日志;
  • LOG_BLOCK_CHECKPOINT_NO 占用4 字节,表示该 log block 最后被写入时的检查点第4 字节的值。
  • log block tailer 只由1 个部分组成,其值和LOG_BLOCK_HDR_NO 相同,并在函数log_block_init 中被初始化。

InnoDB的存储管理是基于页的,redo log file也是基于页的,并且不同类型的语句日志记录不同,但首部是相同的:

  • type:日志类型
  • space:表空间的ID
  • page_no:页的偏移量
  • offset:每个重做日志的数据部分,恢复时需要调用相应的函数进行解析

  • write pos:表示 redo log 当前记录的日志序列号LSN(log sequence number)
  • check point:当数据页已经刷盘后,更新check point。
  • write pos 到 check point之间的部分:是 redo log 空着的部分,用于记录新的操作;
  • check point 到 write pos 之间:是 redo log 待落盘的数据页更改记录;
  • 当 write pos 追上 check point 时,这时候不能再执行新的更新,得停下来,同步到磁盘,推动 check point 向前移动,空出位置再记录新的日志。

LSN是日志序列号,有三个含义:①redo log的日志总量 ②checkpoint的位置 ③页的版本

在前面我们已经提到,写入重做日志文件的操作不是直接写,而是先写入一个重做日志缓冲(redo log buffer)中,然后按照一定的条件顺序地写入日志文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9IHyzQZA-1671368798702)(null)]

从重做日志缓冲往磁盘写入时,是按512个字节,也就是一个扇区的大小进行写入。因为扇区是写入的最小单位,因此可以保证写入必定是成功的,因此在重做日志的写入过程中不需要有doublewrite

启动innodb的时候,不管上次是正常关闭还是异常关闭,总是会进行恢复操作。恢复时,会先检查数据页中的LSN,如果这个LSN小于write pos,说明在redo log上记录着数据页上尚未完成的操作,接着就会从最近的一个check point出发,开始同步数据。在check point和write pos中已经提交的事务会进行redo,没有提交的事务会进行undo。

redo log 是 InnoDB 引擎所特有的,所以我们如果再使用 InnoDB 引擎创建表时,如果数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe

参数

下列参数影响着重做日志文件的属性∶

  • innodb_log_file_size
  • innodb_log_files_in_group
  • innodb_mirrored_log_groups
  • innodb_log_group_home_dir

参数 innodb_log_file_size指定每个重做日志文件的大小。在 InnoDB1.2.x 版本之前,重做日志文件总的大小不得大于等于 4GB,而1.2.x 版本将该限制扩大为了512GB。

参数innodb_log_files_in_group 指定了日志文件组中重做日志文件的数量,默认为 2。

参数 innodb_mirrored_log_groups指定了日志镜像文件组的数量,默认为1,表示只有一个日志文件组,没有镜像。若磁盘本身已经做了高可用的方案,如磁盘阵列,那么可以不开启重做日志镜像的功能。

参数innodb_log_group_home_dir指定了日志文件组所在路径,默认为 ./,表示在 MySQL数据库的数据目录下。

上面介绍了innodb_log_file_size参数用来决定每个重做日志文件的大小。重做日志文件的大小设置对于InnoDB存储引擎的性能有着非常大的影响:

  • 一方面,重做日志文件不能设置的太大,如果设置得很大,在恢复时可能需要很长的时间;
  • 另一方面,重做日志文件又不能设置的太小,否则可能导致一个事务的日志需要多次切换重做日志文件,此外,重做日志文件太小会导致频繁地发生async checkpoint,导致性能的抖动。

例如,用户可能在错误日志中看到如下警告信息:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BDI0jGch-1671368798772)(null)]

上面的错误集中在前两行。这是因为重做日志有一个capacity变量,该值代表了最后的检查点不能超过这个阈值,如果超过则必须将缓冲池中脏页列表中的部分脏数据写回磁盘,这时会导致用户线程的阻塞。

Redo Log和Binlog

Q:MySQL 为什么会有两份日志?
A:binlog日志只用于归档,只依靠binlog是没有crash-safe能力的。但只有redo log也不行,因为redo log是InnoDB特有的,且日志上的记录落盘后会被覆盖掉。因此需要binlog和redo log二者同时记录,才能保证当数据库发生宕机重启时,数据不会丢失。

这两种日志有以下三点不同:

  • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,会记录所有与 MySQL数据库有关的日志记录,所有引擎都可以用,包括 InnoDB、MyISAM、Heap 等其他存储引擎的日志;
  • redo log 是物理日志,记录的是 在某个数据页上做了什么修改;binlog 是逻辑日志,记录的都是关于一个事务的具体操作内容
  • redo log中每个事务对应多个日志记录,bin log中仅在事务提交时记录一次;
  • redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。「追加写」是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志;
  • binlog适用于主从复制和数据恢复,redo log适用于崩溃恢复(crash-safe)。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值