MySQL
MySQL 执行流程和缓冲池
MySQL 事务
MySQL 日志–redo log
MySQL 日志–undo log
MySQL日志--redo log
本文参考尚硅谷康师傅的 MySQL课程
1、为什么需要 redo log
1.1、直接把事务的更改刷新到磁盘的缺点
InnoDB
是以页为单位对磁盘进行I/O
,当我们对页面的修改内容较少(例如一个字节)时,会浪费I/O
性能- 一个事务中可能包含很多语句,改事务修改的业务可能不相邻,这就意味着在将某个事务修改的
Buffer Pool
中的页面刷新到磁盘时需要进行很多的随机I/O
,随机Io比顺序I/O
要慢
1.2、WAL
InnoDB
引擎的事务采用了WAL技术(Write-Ahead Logging ),这种技术的思想就是先写日志,再写磁盘,只有日志写入成功,才算事务提交成功,这里的日志就是redo log
1.3、redo log 的优点
- redo log 是顺序写入磁盘的
- 事务执行过程中,redo log 不断记录
小贴士:
redo log
跟bin log
的区别,redo log
是存储引擎层产生的,而bin log
是数据层产生的。假设一个事务,对表做10万行的记录插入,在这个过程中,一直不断的往redo log
顺序记录,而bin log
不会记录,直到这个事务提交,才会一次写入到bin log
文件中。
2、redo log 的组成
2.1、redo log buffer
在服务器启动时就向操作系统申请了一大片称之为
redo log buffer
的连续内存空间。这片内存空间被划分成若干个连续的redo log block
。一个redo log block
占用512字节
大小。
参数设置:
innodb_log_buffer_size:
redo log buffer 大小,默认 16M ,最大值是4096M,最小值为1M。
mysql> show variables like '%innodb_log_buffer_size%';
+------------------------+----------+
| Variable_name | Value |
+------------------------+----------+
| innodb_log_buffer_size | 16777216 |
+------------------------+----------+
2.2、redo log file
保存在磁盘中,是持久的
redo log 流转过程:
第1步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
第2步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
第3步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加写的方式
第4步:定期将内存中修改的数据刷新到磁盘中
3、redo log 的刷盘策略
redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后以 一定的频率
刷入到真正的redo log file 中。这里一定的频率就是刷盘策略
注意:
redo log buffer
刷盘到redo log file
的过程并不是真正的刷到磁盘中去,只是刷入到文件系统缓存(page cache)
中去(这是现代操作系统为了提高文件写入效率做的一个优化),真正的写入会交给系统自己来决定(比如page cache足够大了)。那么对于InnoDB来说就存在一个问题,如果交给系统来同步,同样如果系统宕机,那么数据也丢失了(虽然整个系统宕机的概率还是比较小的)
innodb_flush_log_at_trx_commit
,该参数控制 commit提交事务时,如何将 redo log buffer 中的日志刷新到 redo log file 中。它支持三种策略
- 设置为0 :表示每次事务提交时不进行刷盘操作。(系统默认master thread每隔1s进行一次 redo log 的同步)
- 设置为1 :表示每次事务提交时都将进行同步,刷盘操作( 默认值 )
- 设置为2 :表示每次事务提交时都只把 redo log buffer 内容写入 page cache,不进行同步。由os自己决定什么时候同步到磁盘文件。
show variables like 'innodb_flush_log_at_trx_commit';
mysql> show variables like 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1 |
+--------------------------------+-------+
1 row in set (0.00 sec)
三种策略下的示意图
4、redo log file
4.1、参数设置
innodb_log_group_home_dir
: 指定 redo log 文件组所在的路径,默认值为./
innodb_log_files_in_group
: 指明redo log file的个数
mysql> 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。
mysql> show variables like 'innodb_log_file_size';
+----------------------+----------+
| Variable_name | Value |
+----------------------+----------+
| innodb_log_file_size | 50331648 |
+----------------------+----------+
根据业务修改其大小,以便容纳较大的事务。编辑my.cnf文件并重启数据库生效,如下所示
[root@localhost ~]# vim /etc/my.cnf
innodb_log_file_size=200M
4.2、日志文件组
从上边的描述中可以看到,磁盘上的redo日志文件不只一个,而是以一个日志文件组的形式出现的。这些文件以ib_logfile[数字]
(数字可以是0、1、2…)的形式进行命名,每个的redo日志文件大小都是一样的。
在将redo日志写入日志文件组时,是从ib_logfile0
开始写,如果ib_logfile0
写满了,就接着ib_logfile1
写。同理,ib_logfile1
.写满了就去写ib_logfile2
,依此类推。如果写到最后一个文件该咋办?那就重新转到ib_logfile0
继续写,所以整个过程如下图所示:
总共的redo日志文件大小: innodb_log_file_size
× innodb_log_files_in_group
采用循环使用的方式向redo日志文件组里写数据的话,会导致后写入的redo日志覆盖掉前边写的redo日志?当然!所以InnoDB的设计者提出了checkpoint
的概念。
4.3、checkpoint
在整个日志文件组中还有两个重要的属性,分别是write pos
、checkpoint
write pos
:是当前记录的位置,一边写一边后移checkpoint
:是当前要擦除的位置,也是往后推移
每次刷盘redo log记录到日志文件组中,write pos位置就会后移更新。每次MySQL加载日志文件组恢复数据时,会清空加载过的redo log记录,并把 checkpoint后移更新。write pos和checkpoint之间的还空着的部分可以用来写入新的redo log记录。
如果 write pos 追上 checkpoint ,表示日志文件组满了,这时候不能再写入新的 redo log记录,MySQL 得停下来,清空一些记录,把 checkpoint 推进一下。
5、小结
InnoDB的更新操作采用的是Write Ahead Log(预先日志持久化)策略,即先写日志,再写入磁盘。