概念
redo-log
- redo log 是在崩溃恢复期间使用的
disk-based
数据结构,用于纠正由不完整的transactions
写入的数据。 - 用来存储 MySQL 在 DML 操作时的数据页变化过程及 LSN(日志序列号),属于物理日志。
- redo log 是 InnoDB 引擎特有的。
- redo log 是循环写的,空间固定会用完
WAL(Write-Ahead Logging)
日志优先写的方式实现持久化
当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到redo-log
里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做
crash-safe
Mysql 进程异常重启了,系统会自动去检查redo log
,将未写入到 Mysql 的数据从redo log
恢复到 Mysql 中去。当数据库发生异常重启时,系统会自动定位到上次checkpoint
的位置,同时,每个数据页中也存在一个LSN
,当redo log
中的LSN
大于数据页中的 LSN 时,说明重启前redo log
中的数据未完全写入数据页中,那么将从数据页中记录的LSN
开始,从redo log
中恢复数据。
LSN(日志序列号)
redo log
中记录的操作相对应的一个点。
命令
mysql> show variables like '%innodb_log%';
+-----------------------------+----------+
| Variable_name | Value |
+-----------------------------+----------+
| innodb_log_buffer_size | 16777216 |
| innodb_log_checksums | ON |
| innodb_log_compressed_pages | ON |
| innodb_log_file_size | 50331648 |
| innodb_log_files_in_group | 2 |
| innodb_log_group_home_dir | ./ |
| innodb_log_write_ahead_size | 8192 |
+-----------------------------+----------+
7 rows in set (0.09 sec)
文件位置
innodb_log_group_home_dir
: 定义了InnoDB log files
的目录路径。如果未配置此选项,则会在 MySQL 数据目录(DATADIR)
中创建InnoDB log files
。
-rw-r-----. 1 mysql mysql 50331648 7月 19 22:37 ib_logfile0
-rw-r-----. 1 mysql mysql 50331648 7月 19 16:21 ib_logfile1
InnoDB
不创建目录,因此在启动服务器之前,请确保log
目录存在。
文件大小
innodb_log_file_size
: 默认5MB
(以字节为单位)
文件个数
innodb_log_files_in_group
: 默认2
个
write pos
是当前记录的位置,一边写一边后移。checkpoint
是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
write pos
和checkpoint
之间的是还空着的部分,可以用来记录新的操作。如果write pos
追上checkpoint
,表示文件满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把checkpoint
推进一下。
注意事项
innodblog_file_size * innodblogfiles_ingroup
的组合大小不能略小于512GB
。一对255GB log files
接近限制但不超过限制。通常,log files
的组合大小应该足够大,以至于服务器可以平滑工作负载活动中的高峰和低谷, value 越大,缓冲池中需要的检查点刷新活动就越少,从而节省了磁盘 I/O。
生命周期
两段提交
- 执行器先找到
ID=2
这一行,ID 是主键,引擎直接用树搜索找到这一行。如果ID=2
这一行所在的数据页本身就在内存中,就直接返回给执行器。否则,需要先从磁盘读到内存。然后再返回。 - 执行器拿到引擎给的行数据,把这个值
+1
,得到新的一行数据,再调用引擎写入这行新数据。 - 引擎将这行新数据更新到内存中,同时将这个更新操作记录到
redo log
中,此时redo log
处于prepare
状态。然后告知执行器执行完成了,随时可以提交事务。 - 执行器生成这个操作的
binlog
,并把binlog
写入磁盘。 - 执行器调用引擎的提交事务接口,引擎把刚写入的
redo log
改成commit
状态,更新完成。
不用两阶段提交会出现的问题
假设当前ID=2
的行,字段c
的值是0
,再假设执行 update 语句过程中在写完第一个日志后,第二个日志还没有写完期间发生了crash
。
-
先写
redo log
后写binlog
。假设在redo log
写完,binlog
还没有写完的时候,MySQL 进程异常重启。由于redo log
写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。但是由于binlog
没写完就crash
了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的binlog
里面就没有这条语句。如果需要用这个binlog
来恢复临时库的话,由于这个语句的binlog
丢失,这个临时库就会少了这一次更新,恢复出来的这一行c
的值就是0
,与原库的值不同。 -
先写
binlog
后写redo log
。如果在binlog
写完之后crash
,由于redo log
还没写,崩溃恢复以后这个事务无效,所以这一行c
的值是0
。但是binlog
里面已经记录了把c从0改成1
这个日志。所以,在之后用binlog
来恢复的时候就多了一个事务出来,恢复出来的这一行c
的值就是1
,与原库的值不同。