刷盘策略
- Innodb在写Redo日志的时候,是先写入redo log buffer 中,然后再按照一定频率刷新到redo log file中。
- 这里的 一定频率 有多个选择,这些选择对应的就是不同的刷盘策略.
这里的刷盘不是指将内存中的数据刷新到磁盘,而是指从Redo日志位于内存中的缓冲区(redo log buffer)刷新到位于磁盘中的文件区(redo log file).
- redo log buffer 刷盘到redo log file的过程也不是真正刷到磁盘中去,只是刷入到 文件缓存系统(
page cache
)中去。真正的刷入磁盘会交给操作系统来决定
关键参数
数据库中innodb_flush_log_at_trx_commit
参数就控制了在事务提交时,如何将buffer中的日志数据刷新到file中.
该参数支持三种策略:
- 参数值为0:提交事务也不进行刷盘操作
- 参数值为1:提交事务一次就刷盘一次( 默认刷盘策略)
- 参数值为2:每次提交事务时,只会将buffer中的内容写入页面缓存中,不会在将页面缓存中的数据刷盘到file中。
查看默认的刷盘参数值:
mysql> show variables like 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1 |
+--------------------------------+-------+
1 row in set, 1 warning (0.03 sec)
可以看到参数值为1,即默认提交一次就刷盘一次
这里必须提到的一个情况是,即使我们将参数设为0,什么也不做,在数据库中也有一个后台线程,默认每1秒就帮我们自动刷盘一次:
没有提交的事务在执行过程中写下的redo log记录,也可能会被后台线程刷盘,如下图:
除了后台线程自动刷盘外,还有一种情况也会刷盘,就是当buffer中的记录数据大小达到最大值(16M
)的一半的时候,也会自动刷盘。
总结:
- 事务提交时会刷盘
- 后台线程每隔1秒自动刷盘
- 记录达到buffer空间一半时刷盘
不同刷盘策略演示
参数值为1时,提交一次刷盘一次
参数为2时,将buffer中的数据刷新到page cache中间缓存中,不会进一步更新到file中
我们分别对参数为1和参数为2时进行操作,看相同操作下哪个用掉的时间更短。
参数1演示
创建数据库
CREATE DATABASE atguigudb3;
使用数据库
USE atguigudb3;
创建test_load表作为初始数据
CREATE TABLE test_load(
a INT,
b CHAR(80)
)ENGINE=INNODB;
使用存储过程添加数据
这里是每insert一次就提交一次事务,过会调用储存过程是可以看看多长时间
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;
先查看一下当前刷盘参数是多少
mysql> SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1 |
+--------------------------------+-------+
1 row in set, 1 warning (0.03 sec)
当前是1,则看1的过程
调用存储过程生成数据 3万次操作就是3万个事务提交
mysql> CALL p_load(30000);
Query OK, 0 rows affected (27.41 sec)
可以看到,参数为1时用掉了27秒
参数2演示
先清空一下数据
mysql> truncate table test_load;
Query OK, 0 rows affected (0.03 sec)
查看是否清空 为0 清空
mysql> select count(*) from test_load;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
修改为2
mysql> SET GLOBAL innodb_flush_log_at_trx_commit = 2;
Query OK, 0 rows affected (0.00 sec)
查看是否修改为2
mysql> SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 2 |
+--------------------------------+-------+
1 row in set, 1 warning (0.00 sec)
开始操作
mysql> CALL p_load(30000);
Query OK, 0 rows affected (19.52 sec)
参数为2时,用掉了19.5秒
参数0演示
直接设置为0
mysql> set global innodb_flush_log_at_trx_commit=0;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 0 |
+--------------------------------+-------+
1 row in set, 1 warning (0.00 sec)
mysql> call p_load(30000);
Query OK, 0 rows affected (14.81 sec)
相同操作 参数为0 用掉了14.8秒
从演示结果来看,参数为0和2用时差不多,即策略一和策略三是相同的,默认的策略一即参数为1用时最多,提交一次事务就刷盘一次虽然用时多,但最能保证事务的持久性
Redo log buffer缓存区
- 对底层页面的一个原子访问称为一个mtr,即迷你事务(
Mini-Transaction
) - 一个
MTR
包含一组redo日志,这一组日志是不可分割的一个整体(即使系统崩溃也不可分割) - 一个事务可以包含若干个
MTR
,一个MTR
可以包含若干个redo日志:
- Redo日志的log buffer 区域是一个整体的存储空间,这个空间又被划分成一个个的存储块,写入日志时是顺序写入,这些存储块也是按顺序被填满
buf_free
变量标记当前到了那个块
- 一个事务中包含多个MTR,就包含多组redo日志,这些组日志会并发的插入到存储块中,没有顺序
存储块结构
- 一个存储块是512个字节,由日志头、日志体、体制尾组成。头占12,尾占8,所以一个块的实际存储空间是496个字节
Redo log file日志文件
- MySQL默认目录中,有两个文件
ib_logfile0
和ib_logfile1
,每次刷盘都是将数据刷新到这两个文件内 - Redo日志文件有很多个,一般以日志文件组的形式出现,文件统一命名,格式是ib_logfile+数字,从0开始
- 日志文件组中每个文件大小相同
- 每次写入从0开始,然后是1,2,3…
日志文件组属性
日志文件组有两个重要属性:
write pos
:标识当前记录位置,并向后移动checkpoint
:擦除当前位置的记录,并向后移动
这两个属性就像是两个指针,在同一个环上移动,一个标记当前数据写到哪了,另一个是擦除数据。
- 每次刷盘时,会添加新的数据记录到文件组中,
write pos
会后移 - 每次恢复数据时,会清空加载过的数据记录,
checkpoint
会后移, - 两个指针之间空的部分写入新记录
总结
经过上篇和本片博客的介绍,我们已经了解了Redo日志的作用和刷盘原理、存储形式等,包括Innodb存储引擎的日志持久化策略
整个过程如下: