innodb_flush_log_at_trx_commit=0
小结: innodb_flush_log_at_trx_commit=o
为0时,master thread中每1秒进行一次重做日志的fsync操作,因此实例crash最多丢失1秒钟内的事务。( master thread是负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性)
数值0话,是一种折中的做法,它的IO效率理论是高于1的,低于2的,这种策略也有丢失数据的风险,也无法保证D。
2.举例
比较innodb_flush_log_at_trx_commit对事务的影响。
#10-事务日志 USE atguigudb3; CREATE TABLE test_load( a INT, b CHAR(80) )ENGINE=INNODB; #创建存储过程,用于向test_load中添加数据 DELIMITER// CREATE PROCEDURE p_load(COUNT INT UNSIGNED) BEGIN DECLARE s INT UNSIGNED DEFAULT 1; DECLARE c CHAR(80)DEFAULT REPEAT('a',80); WHILE s<=COUNT DO INSERT INTO test_load SELECT NULL,c; COMMIT; SET s=s+1; END WHILE; END // DELIMITER; #测试1: #设置并查看:innodb_flush_log_at_trx_commit SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit'; #set GLOBAL innodb_flush_log_at_trx_commit = 1; #调用存储过程 CALL p_load(30000); #1min 28sec #测试2: TRUNCATE TABLE test_load; SELECT COUNT(*) FROM test_load; SET GLOBAL innodb_flush_log_at_trx_commit = 0; SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit'; #调用存储过程 CALL p_load(30000); #37.945 sec #测试3: TRUNCATE TABLE test_load; SELECT COUNT(*) FROM test_load; SET GLOBAL innodb_flush_log_at_trx_commit = 2; SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit'; #调用存储过程 CALL p_load(30000); #45.173 sec
下表显示了在innodb_flush_log_at_trx_commit的不同设置下,调用存储过程p_load插入3万行记录所需的时间:
而针对上述存储过程,为了提高事务的提交性能,应该在将3万行记录插入表后进行一次的COMMIT操作,而不是每插入一条记录后进行一次COMMIT操作。这样做的好处是可以使事务方法在rollback时回滚到事务最开始的确定状态。
虽然用户可以通过设置参数innodb_flush_log_at_trx_commit为0或2来提高事务提交的性能,但需清楚,这种设置方法丧失了事务的ACID特性。
1.7 写入redo log buffer 过程
1.补充概念:Mini-Transaction
MySQL把对底层页面中的一次原子访问的过程称之为一个Mini-Transaction,简称mtr,比如,向某个索引对应的B+树中插入一条记录的过程就是一个Mini-Transaction。一个所谓的mtr可以包含一组redo日志,在进行崩溃恢复时这一组redo日志作为一个不可分割的整体。
一个事务可以包含若干条语句,每一条语句其实是由若干个mtr组成,每一个mtr又可以包含若干条redo日志,画个图表示它们的关系就是这样:
2. redo 日志写入log buffer
向log buffer中写入redo日志的过程是顺序的,也就是先往前边的block中写,当该block的空闲空间用完之后再往下一个block中写。当想往log buffer中写入redo日志时,第一个遇到的问题就是应该写在哪个block的哪个偏移量处,所以InnoDB的设计者特意提供了一个称之为buf_free的全局变量,该变量指明后续写入的redo日志应该写入到 log buffer中的哪个位置,如图所示:
一个mtr执行过程中可能产生若干条redo日志,这些redo日志是一个不可分割的组,所以其实并不是每生成一条redo日志,就将其插入到log buffer中,而是每个mtr运行过程中产生的日志先暂时存到一个地方,当该mtr结束的时候,将过程中产生的一组redo日志再全部复制到log bulffer中。假设有两个名为T1、T2的事务,每个事务都包含2个mtr,我们给这几个mtr命名一下;
事务T1的两个mtr分别称为mtr_T1_1和mtr_T1_2
事务T2的两个mtr分别称为mtr_T2_1和mtr_T2_2
每个mtr都会产生一组redo日志,用示意图来描述一下这些mtr产生的日志情况:
不同的事务可能是并发执行的,所以T1、T2之间的mtr可能是交替执行的。每当一个mtr执行完成时,伴随该mtr生成的一组redo日志就需要被复制到log buffer中,也就是说不同事务的mtr可能是交替写入log buffer的,我们画个示意图(为了美观,把一个mtr中产生的所有的redo日志当作一个整体来画):
有的mtr产生的redo日志量非常大,比如1mtr_t1_2
产生的redo日志占用空间比较大,占用了3个block来存储。
3. redo log block的结构图
一个redo log block是由日志头
、日志体
、日志尾
组成。日志头占用12字节,日志尾占用8字节,所以一个block真正能存储的数据就是512-12-8=492字节。
为什么一个block设计成512字节?
这个和磁盘的扇区有关,机械磁盘默认的扇区就是512字节,如果要写入的数据大于512字节,那么要写入的扇区肯定不止一个,这时就要涉及到盘片的转动,找到下一个扇区,假设现在需要写入两个扇区A和B,如果扇区A写入成功,而扇区B写入失败,那么就会出现非原子性的写入,而如果每次只写入和扇区的大小一样的512字节,那么每次的写入都是原子性的
真正的redo日志都是存储到占用496
字节大小的log block body
中,图中的log block header
和log block trailer
存储的是一些管理信息。我们来看看这些所谓的管理信息
都有什么
log block header的属性分别如下:
LOG_BLOCK_HDR_NO : log buffer是由log block组成,在内部log buffer就好似一个数组,因此LOG_BLOCK_HDR_NO用来标记这个数组中的位置。其是递增并且循环使用的,占用4个字节,但是由于第—位用来判新是否是flush bit,所以最大的值为2G。
LOG_BLOCK_HDR_DATA_LEN∶表示block中已经使用了多少字节,初始值为12(因为log block body从第12个字节处开始)。随着往block中写入的redo日志越来也多,本属性值也跟着增长。如果log block body已经被全部写满,那么本属性的值被设置为512
LOG_BLOCK_FIRST_REC_GROUP :一条redo日志也可以称之为一条redo日志记录(redo log record),一个mtr会生产多条redo日志记录,这些redo日志记录被称之为一个redo日志记录组(redo log record group)。LOG_BLOCK_FIRST_REC_GROUP就代表该block中第一个mtr生成的redo日志记录组的偏移量(其实也就是这个block里第一个mtr生成的第一条redo日志的偏移量)。如果该值的大小
LOG_BLOCK_HDR_DATA_LEN相同,则表示当前log block不包含新的日志。
LOG_BLOCK_CHECKPOINT_NO:占用4字节,表示该log block最后被写入时的checkpoint。
log block trailer中属性的意思如下:
LOG_BLOCK_CHECKSUN:表示block的校验值,用于正确性校验(其值和LOG_BLOCK_HDR_NO相同),暂时不关心它。
1.8 redo log file
1.相关参数设置
innodb_log_group_home_dir :指定 redo log 文件组所在的路径,默认值为 ./ ,表示在数据库的数据目录下。MySQL的默认数据目录( var/lib/mysql )下默认有两个名为 ib_logfile0 和ib_logfile1 的文件,log buffer中的日志默认情况下就是刷新到这两个磁盘文件中。此redo日志文件位置还可以修改。
mysql> show variables like 'innodb_log_group_home_dir'; +---------------------------+-------+ | Variable_name | Value | +---------------------------+-------+ | innodb_log_group_home_dir | ./ | +---------------------------+-------+ 1 row in set (0.00 sec)
- innodb_log_files_in_group:指明redo log file的个数,命名方式如:ib_logfile0,iblogfile1…iblogfilen。默认2个,最大100个。
show variables like 'innodb_log_files_in_group'; /* +---------------------------+-------+ | Variable_name | Value | +---------------------------+-------+ | innodb_log_files_in_group | 2 | +---------------------------+-------+ */ #ib_logfile0 #ib_logfile1
innodb_flush_log_at_trx_commit:控制 redo log 刷新到磁盘的策略,默认为1。
innodb_log_file_size:单个 redo log 文件设置大小,默认值为 48M 。最大值为512G,注意最大值指的是整个redo log 系列文件之和,即(innodb_log_files_in_group * innodb_log_file_size)不能大于最大值512G。
show variables like 'innodb_log_file_size'; /* +----------------------+----------+ | Variable_name | Value | +----------------------+----------+ | innodb_log_file_size | 50331648 | +----------------------+----------+ */
根据业务修改其大小,以便容纳较大的事务。编辑my.cnf文件并重启数据库生效,如下所示
[root@centos7-mysql-1 mysql]#vim /etc/my.cnf innodb_log_file_size=200M
在数据库实例更新比较频繁的情况下,可以适当加大 redo log组数和大小。但也不推荐redo log设置过大,在MySQL前溃恢复时会重新执行REDO日志中的记录。