
爆款秒杀
Redo Log
从某种程度上讲,事务的隔离性是由锁和 MVCC 机制实现的,原子性和持久性是由 Redo Log 实现的,一致性是由 Undo Log 实现的。
基本概念
Redo Log 也称作重做日志,他是在 InnoDB 存储引擎中产生的,用来保证事务的原子性和持久性。Redo Log 主要记录的是物理日志,也就是对磁盘上的数据进行修改。
Redo Log 主要包含两部分:一部分是内存中的日志缓冲,称作 Redo Log Buffer, 这部分日志比较容易丢失;另一部分是存在磁盘上的重做日志文件,称作 Redo Log File, 这部分日志是持久化到磁盘上的,不容易丢失。
基本原理
Redo Log 能够保证事务的原子性和持久性。在 Mysql 发生故障时,尽力避免内存中的脏页数据写入数据库表的 IBD 文件中,在重启 MySQL 服务时,可以根据 Redo Log 恢复事务已经提交但是还未写入 IBD 的数据。从而对事务提交的数据进行持久化操作。
刷盘规则
开启事务,发出提交事务指令后是否刷新日志由变量 innodb_flush_log_at_trx_commit
决定每秒刷新一次,刷新日志的频率由变量 innodb_flush_log_at_timeout
的值决定,默认是 1s, 刷新日志的频率和是否执行了 commit 操作无关当 Log Buffer 中已经使用内存超过一半时,会触发刷盘操作 当事务中存在 检查点
时,在一定程度上代表了刷写到磁盘时日志所处的 LSN(日志逻辑序列号) 的位置。
下面对第一条进行详细的介绍:
当事务提交时,需要先将事务日志写入 Log Buffer 中,这些写入 Log Buffer 的日志并不是随着事务的提交立刻写入磁盘的,而是根据一定的规则,从而保证了 Redo Log 文件中数据的持久性。这种刷盘规则可以由 innodb_flush_log_at_trx_commit
变量控制,他的取值有三个 0、1、2 默认为 1,每个值有不同的输盘规则。
0: 每次提交事务时,不会将 Log Buffer 中的日志写入 OS buffer, 而是通过一个单独的线程,每秒写入 OS buffer 并调用系统的 fsync() 函数写入磁盘的 Redo Log File, 这种方式不是实时写磁盘的, 而是每隔 1s 写一次日志,如果系统崩溃,可能会丢失 1s 的数据。 1: 每次提交事务都会将 Log Buffer 中的日志写入 OS buffer 中,并且会调用 fsync() 函数将日志写入 Redo Log File 中,这种方式虽然不会再崩溃时丢失数据,但是性能比较差。也是这个变量的默认值。 2: 每次提交事务时,都只是将数据写入 os buffer 中,之后每隔 1s ,通过 fsync() 函数将 os buffer 中的数据写入 Redo Log 文件中。
Redo Log 刷盘策略的最佳实践
# 查询刷盘策略,默认是1
show variables like 'innodb_flush_log_at_trx_commit';
# 创建刷盘数据表
create table flush_disk_test (
id int not null auto_increment,
name varchar(20),
primary key (id)
)engine = INNODB;
# 创建一个存储过程,往上面的表中添加数据
drop procedure if exists insert_data_to_flush_disk_test;
delimiter $$
create procedure insert_data_to_flush_disk_test(i int)
begin
declare s int default 1;
declare n varchar(50) default 'tom';
while s <= i do
start transaction;
insert into flush_disk_test(name) values(n);
commit;
set s = s + 1;
end while;
end $$
delimiter ;
# 调用存储过程插入 十万条数据
call insert_data_to_flush_disk_test (100000);
call insert_data_to_flush_disk_test_improve (100000);
# 查看插入的数据是不是十万
select count(*) from flush_disk_test;
# 清空数据库表中插入的数据
truncate flush_disk_test;
# 修改刷盘策略为 0
set global innodb_flush_log_at_trx_commit=0;
# 优化存储过程,把事务的开启和关闭放到循环体外面
# 创建一个存储过程,往上面的表中添加数据
drop procedure if exists insert_data_to_flush_disk_test_improve;
delimiter $$
create procedure insert_data_to_flush_disk_test_improve(i int)
begin
declare s int default 1;
declare n varchar(50) default 'tom';
start transaction;
while s <= i do
insert into flush_disk_test(name) values(n);
set s = s + 1;
end while;
commit;
end $$
delimiter ;
Redo Log 的写入机制
Redo Log 记录的是物理日志,其文件内容是以循环的方式写入的,一个文件写满了就写入另一个文件,最后一个文件写满了就会向第一个文件写入,并且是覆盖写。
Write Pos 是数据表中当前记录所在的位置,随着写入,这个位置逐渐向后移动,最后一个文件写满后,这个位置移动到第一个文件的开始处。 CheckPoint 是当前要擦除的位置,这个位置也是向后移动的,擦除之前要将数据更新到数据文件中。 Write Pos 和 CheckPoint 之间存在间隔,间隔表示还可以记录新的操作。如果 Write Pos 写入较快,追上了擦除的位置,则表示已经写满,不再向 Redo Log 文件中写数据了,此时需要停止写入,擦除一些数据。

往期推荐
往期推荐

点个在看,你最好看

文章转载自Java之康庄大道,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




