目录
一、redo log的概念
redo log,重做日志,也叫重放日志。和undo log回滚日志一样,都是在数据库发生意外时用来进行数据恢复的,通过前面一篇文章对undo log的总结,我们都知道undo log记录的是数据更新前的样子,主要保证事务的原子性;而redo log则记录的是事务执行过程中的修改情况,redo log主要保证事务的持久性。
当数据库对数据做修改的时候,需要把数据页从磁盘读到buffer pool中,然后在buffer pool中进行修改,那么这个时候buffer pool中的数据页就与磁盘上的数据页内容不一致,称buffer pool的数据页为dirty page 脏数据,如果这个时候发生非正常的DB服务重启,那么这些数据还没在内存,并没有同步到磁盘文件中(注意,同步到磁盘文件是个随机IO),也就是会发生数据丢失,如果这个时候,能够在有一个文件,当buffer pool 中的数据页变更结束后,把相应修改记录记录到这个文件(注意,记录日志是顺序IO),那么当DB服务发生crash,进行恢复DB的时候,可以根据这个文件的记录内容,重新持久化刷新到磁盘文件,保持数据的一致性。
这个文件其实就是redo log ,用于记录数据修改后的记录,顺序记录,主要用于数据的持久化操作。
二、redo log的作用
- 1、保证事务的持久性
如果buffer pool缓冲池中的脏页【脏数据】还没有进行刷盘的时候,此时数据库发生crash,重启服务后,我们可以通过redo log日志找到需要重放到磁盘文件的那些数据记录。
- 2、提高事务提交的速度
buffer pool缓冲池中的数据直接刷新到磁盘,是一个随机IO,效率较差,而把buffer pool中的数据记录到redo log,是一个顺序IO,可以提高事务提交的速度。例如:
我们执行了一条更新语句:
update user set name = 'lisi' where id = 1 --更新前name = 'zhangsan'
此时redo log就会用来存在name = 'lisi'这条更新后的新纪录,如果在刷盘时发生异常,我们可以通过redo log找到这条记录,然后进行重放操作,以保证事务的持久性。
三、redo log的组成
redo log由两部分组成:
- redo log buffer日志缓存
重做日志缓存(redo log buffer)存在于内存中,容易发生丢失。
- redo log file日志文件
重做日志文件(redo log file)存在于磁盘中,不容易发生丢失。
从redo log buffer写日志到磁盘的redo log file中,过程如下:
为了确保每次日志都能写入到事务日志文件中,在每次将log buffer中的日志写入日志文件的过程中都会调用一次操作系统的fsync操作(即fsync()系统调用)。因为MariaDB/MySQL是工作在用户空间的,MariaDB/MySQL的log buffer处于用户空间的内存中。要写入到磁盘上的log file中(redo:ib_logfileN文件,undo:share tablespace或.ibd文件),中间还要经过操作系统内核空间的os buffer,调用fsync()的作用就是将OS buffer中的日志刷到磁盘上的log file中。
四、redo log的工作原理
和undo log相反,redo log记录的是新数据的备份。在事务提交前,只要将redo log持久化即可,
不需要将数据持久化,不需要将数据持久化,不需要将数据持久化,重要的事情说三遍!。当系统崩溃时,虽然数据没有持久化,但是redo log已经持久化到磁盘中,系统可以根据redo log的内容,将所有数据恢复到最新的状态。
同样,通过一张图来理解redo log的工作原理。
下面描述一下redo事务的简化过程。
假设有A、B两个数据,值分别为1,2,开始一个事务,事务的操作内容为:把1修改为3,2修改为4,那么实际的记录如下(简化):
- A. 事务开始.
- B. 记录A=1到undo log.
- C. 修改A=3.
- D. 记录A=3到redo log.
- E. 记录B=2到undo log. -------记录undo log回滚日志
- F. 修改B=4. -------修改数据
- G. 记录B=4到redo log. -------记录redo log重做日志
- H. 将redo log写入磁盘。 -------redo log刷盘持久化
- I. 事务提交 -------提交事务
如上过程,我们可以总结出来undo + redo事务的特点:
- A. 为了保证持久性,必须在事务提交前将redo log日志持久化。
- B. 数据不需要在事务提交前写入磁盘,而是缓存在内存中。
- C. redo log保证事务的持久性。
- D. undo log保证事务的原子性。
- E. 数据必须要晚于redo log写入持久存储。
五、redo log的相关参数
- innodb_flush_log_at_trx_commit
控制commit动作是否刷新log buffer到磁盘。该变量有3种值:0、1、2,默认为1。
1、innodb_flush_log_at_trx_commit=0【延迟写】:事务提交时不会将log buffer中日志写入到os buffer,然后每秒调用fsync()写入到log file on disk磁盘文件中。
这种情况下如果系统崩溃,会丢失1秒钟的数据。
2、innodb_flush_log_at_trx_commit=1【实时写,实时刷】:事务每次提交,会保存到log buffer,接着保存到os buffer操作系统缓存,并调用fsync()刷到log file on disk磁盘文件中。
这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO的性能较差。
3、innodb_flush_log_at_trx_commit=2【实时写,延迟刷】:每次事务提交,数据不写到log buffer,仅写入到os buffer,然后是每秒调用fsync()将os buffer中的日志写入到log file on disk磁盘文件中。
如下图:
正常情况下,设置参数值为0或者2能提供插入的效率,但在故障的时候可能会丢失1秒钟数据。并且参数值为2和0的时候差距并不大,因为它们都是每秒从os buffer刷到磁盘,它们之间的时间差体现在log buffer刷到os buffer上。因为将log buffer中的日志刷新到os buffer只是内存数据的转移,并没有太大的开销,所以每次提交和每秒刷入差距并不大。但值为1的性能却会差很多。
- innodb_log_buffer_size
指定 log buffer【redo log缓存区】的大小,默认8M。延迟事务日志写入磁盘,把redo log 放到该缓冲区,然后根据 innodb_flush_log_at_trx_commit参数的设置,再把日志从buffer 中flush 到磁盘中。
- innodb_log_file_size
指定事务日志的大小,默认5M。
- innodb_log_files_group =2
log group表示的是redo log group,一个组内由多个大小完全相同的redo log file组成。innodb_log_files_group指定事务日志组中的事务日志文件个数,默认2个,最大是100个。命名方式如:ib_logfile0,iblogfile1... iblogfilen。
在innodb将log buffer中的redo log block刷到这些log file中时,会以追加写入的方式循环轮训写入。即先在第一个log file(即ib_logfile0)的尾部追加写,直到满了之后向第二个log file(即ib_logfile1)写。当第二个log file满了会清空一部分第一个log file继续写入。
- innodb_log_group_home_dir =./
指定事务日志组路径,当前目录表示数据目录。
六、redo log与undo log比较
- 1、undo log和redo logo都是InnoDB的功能,都是事务日志。
- 2、redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。
- 3、undo log用来回滚行记录到某个版本。undo log一般是逻辑日志,根据每行记录进行记录。
- 4、undo log保证事务的原子性,而redo log则是保证事务的持久性。undo log在InnoDB中用来实现MVCC多版本控制,执行rollback操作时,undo log可以作为事务回滚的快照读参考,而redo log是备份的最新数据位置,系统冗机时,只要重启mysql服务,就可以将未持久保存的数据持久到磁盘。
七、redo log与二进制日志比较
- 1、redo log是MySQL InnoDB存储引擎产生的,只记录该存储引擎中表的修改,而二进制日志是在数据库层面产生的,所有存储引擎对数据库进行修改都会产生二进制日志。
- 2、二进制日志记录的是对应的SQL,比如记录的该行数据每一列的值是多少多少,而redo log记录的则是物理数据页的修改记录。
- 3、redo log记录的是物理格式的日志,记录了对页的操作,而binlog记录的是逻辑日志,记录的是对应的SQL。
- 4、redo log记录的是数据页的物理变化,因此恢复的时候速度比二进制日志要快很多。另外,redo log具有幂等性,所以多次操作得到同一结果的行为在日志中只记录一次。而二进制日志不具有幂等性,多次操作会全部记录下来,在恢复的时候会多次执行二进制日志中的记录,速度就慢得多。例如,某记录中id初始值为2,通过update将值设置为了3,后来又设置成了2,在事务日志中记录的将是无变化的页,根本无需恢复;而二进制会记录下两次update操作,恢复时也将执行这两次update操作,速度比事务日志恢复更慢。
- 5、二进制日志在提交的时候一次性写入,一次提交对应一条记录,而redo log中是记录的物理页的修改,redo log文件中同一个事务可能多次记录,最后一个提交的事务记录会覆盖所有未提交的事务记录。举个例子,一个事务中插入10万条记录:
insert into user(name, age) values('zs', 20);
insert into user(name, age) values('ls', 30);
//.....插入十万条记录
在这个过程中,MySQL会一直不断的往redo log顺序记录,而binary log则不会记录,直到commit事务,才会一次性写入到bin log日志中。
附录:参考资料
https://www.cnblogs.com/f-ck-need-u/p/9010872.html#auto_id_11
https://www.cnblogs.com/wyy123/p/7880077.html
《MySQL技术内幕(第5版)》