redo log
redo log 用于记录事务操作的变化,记录的是sql语句执行之后数据页上的具体修改内容。
redo log是innodb引擎独有,redo log不是二进制日志
redo log基本概念
https://www.jianshu.com/p/126fbdef0e12
https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html
redo log包括两部分:内存中的日志缓冲redo log buffer、磁盘上的重做日志文件redo log file
基本操作流程
1、redo log就是存储了数据被修改后的值。当提交一个事务时,InnoDB会先把数据写入redo log 日志。
2、然后再去修改内存中buffer pool里面的真正数据页。
3、通过 checkpoint机制写入到 disk。
redo log buffer处于 用户空间 ,数据 需要先写到 内核空间的os buffer ,而os buffer中的数据 写入磁盘log files,需要进行系统调用(sync()操作)
MySQL支持用户自定义在commit时如何将log buffer中的日志刷log file中。这种控制通过变量 innodb_flush_log_at_trx_commit 的值来决定。该变量有3种值:0、1、2,默认为1。但注意,这个变量只是控制commit动作是否刷新log buffer到磁盘。
当设置为1的时候,事务每次提交都会将log buffer中的日志写入os buffer并调用fsync()刷到log file on disk中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。
当设置为0的时候,事务提交时不会将log buffer中日志写入到os buffer,而是每秒写入os buffer并调用fsync()写入到log file on disk中。也就是说设置为0时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失1秒钟的数据。
当设置为2的时候,每次提交都仅写入到os buffer,然后是每秒调用fsync()将os buffer中的日志写入到log file on disk。
参数innodb_log_buffer_size控制redo log buffer大小,默认16mb
日志块(log block)
innodb存储引擎中,redo log以块为单位进行存储的,每个块占512字节,称为redo log block。
log buffer、os buffer、redo log file on disk中,都是以512字节的块存储的。
每个redo log block由3部分组成:日志块头、日志块尾 和 日志主体。
其中日志块头占用12字节,日志块尾占用8字节,日志主体部分只有512-12-8=492字节。
日志块头
日志块头包含4部分:
log_block_hdr_no:(4字节)该日志块在redo log buffer中的位置ID。
log_block_hdr_data_len:(2字节)该log block中已记录的log大小。写满该log block时为0x200,表示512字节。
log_block_first_rec_group:(2字节)该log block中第一个log的开始偏移位置。
lock_block_checkpoint_no:(4字节)写入检查点信息的位置。
log body(redo log的格式)
log body的格式分为4部分:
redo_log_type:占用1个字节,表示redo log的日志类型。
space:表示表空间的ID,采用压缩的方式后,占用的空间可能小于4字节。
page_no:表示页的偏移量,同样是压缩过的。
redo_log_body表示每个重做日志的数据部分,恢复时会调用相应的函数进行解析。
例如insert语句和delete语句写入redo log的内容是不一样的。
redo log group和redo log file
redo log group
redo log group(重做日志文件组),一个组内由多个大小完全相同的redo log file组成。组内redo log file的数量由变量 innodb_log_files_group 决定,默认值为2,即两个redo log file。
可以通过变量 innodb_log_group_home_dir 来定义组的目录,redo log file都放在这个目录下,默认是在datadir下。
在innodb将log buffer中的redo log block刷到这些log file中时,会以追加写入的方式循环轮训写入。
即先在第一个log file(即ib_logfile0)的尾部追加写,直到满了之后向第二个log file(即ib_logfile1)写。当第二个log file满了会清空一部分第一个log file继续写入。
redo log file
在每个组的第一个redo log file中,前2KB记录4个特定的部分,从2KB之后才开始记录log block。
除了第一个redo log file中会记录,log group中的其他log file不会记录这2KB,但是却会腾出这2KB的空间。如下:
redo log file的大小对innodb的性能影响非常大,设置的太大,恢复的时候就会时间较长,设置的太小,就会导致在写redo log的时候循环切换redo log file。
日志文件参数:
SHOW VARIABLES LIKE 'innodb_log_file_size' -- 重做日志文件的大小。
SHOW VARIABLES LIKE 'innodb_log_files_in_group' -- 指定重做日志文件组中文件的数量,默认2
SHOW VARIABLES LIKE 'innodb_log_group_home_dir' -- innodb_log_group_home_dir
日志刷盘的规则
log buffer中未刷到磁盘的日志称为脏日志(dirty log)。
innodb有以下 刷日志到磁盘的规则:
1.发出commit动作时(默认)。
commit发出后是否刷日志由变量 innodb_flush_log_at_trx_commit 控制。
2.每秒刷一次。
由变量 innodb_flush_log_at_timeout 值决定,默认是1秒。
要注意,这个刷日志频率和commit动作无关。
3.当log buffer中已经使用的内存超过一半时。
4.当有checkpoint时,checkpoint在一定程度上代表了刷到磁盘时日志所处的LSN位置。
数据页刷盘的规则
不仅仅是日志需要刷盘,脏数据页也一样需要刷盘
在innodb中,数据刷盘的规则只有一个:checkpoint。
checkpoint触发后,会将buffer中脏数据页和脏日志页都刷到磁盘。
innodb存储引擎中checkpoint分为两种:
sharp checkpoint:在重用redo log文件(例如切换日志文件)的时候,将所有已记录到redo log中对应的脏数据刷到磁盘。
fuzzy checkpoint:一次只刷一小部分的日志到磁盘,而非将所有脏日志刷盘。有以下几种情况会触发该检查点:
master thread checkpoint:由master线程控制,每秒或每10秒刷入一定比例的脏页到磁盘。
flush_lru_list checkpoint:从MySQL5.6开始可通过 innodb_page_cleaners 变量指定专门负责脏页刷盘的page cleaner线程的个数,该线程的目的是为了保证lru列表有可用的空闲页。
async/sync flush checkpoint:同步刷盘还是异步刷盘。例如还有非常多的脏页没刷到磁盘(非常多是多少,有比例控制),这时候会选择同步刷到磁盘,但这很少出现;如果脏页不是很多,可以选择异步刷到磁盘,如果脏页很少,可以暂时不刷脏页到磁盘
dirty page too much checkpoint:脏页太多时强制触发检查点,目的是为了保证缓存有足够的空闲空间。too much的比例由变量 innodb_max_dirty_pages_pct 控制,MySQL 5.6默认的值为75,即当脏页占缓冲池的百分之75后,就强制刷一部分脏页到磁盘。
redo log作用
保证事务的持久性(数据恢复);
对数据进行修改时,是修改内存中的数数据,当内存中的数据当内存中数据还未刷回磁盘就宕机时,可以通过redolog恢复数据,而且redolog只是记录数据的修改,无需将完整的数据刷回磁盘;
redo log 和 bin log 的区别
redolog是物理日志,binlog是逻辑日志;
redolog是innodb引擎的,binlog是 server层的;
redo log 采用循环写的方式记录,当写到结尾时,会回到开头循环写日志。binlog 通过追加的方式记录,当文件大小大于给定值后,后续的日志会记录到新的文件上
redo log适用于崩溃恢复(crash-safe)。binlog适用于主从复制和数据恢复
binlog日志只用于归档,只依靠binlog是没有crash-safe能力的。但只有redo log也不行,因为redo log是InnoDB特有的,且日志上的记录落盘后会被覆盖掉。因此需要binlog和redo log二者同时记录,才能保证当数据库发生宕机重启时,数据不会丢失。