众所周知InnoDB 是一个事务性的存储引擎,在上一小节我们提到事务有4种特性:原子性、一致性、隔离性和持久性,在事务中的操作,要么全部执行,要么全部不做,这就是事务的目的。
那么事务的四种特性到底是基于什么机制实现呢???
-
1、事务的原子性、隔离性由锁机制实现,我们将在后续章节《数据库锁机制》中介绍
-
2、而事务的一致性和持久性由事务的 redo 日志和undo 日志来保证。
| redo log 是重做日志,提供再写入操作,实现事务的持久性; |
| undo log 是回滚日志,提供回滚操作,保证事务的一致性。 |
本文将讨论关于事务中的redo和undo的几个问题:
- redo 日志与undo日志分别用于记录什么?
- redo 如何保证事务的持久性?
- undo 如何保证事务的一致性?
- undo log 是否是redo log的逆过程?
记录的是尚未完成的操作,数据库崩溃则用其重做
Redo log可以简单分为以下两个部分:
- 保存在内存中重做日志的缓冲 (redo log buffer),是易失的
- 保存在硬盘中重做日志文件 (redo log file),是持久的
InnoDB 的更新操作采用的是 Write Ahead Log (预先日志持久化)策略,即先写日志,再写入磁盘。
当一条记录更新时,redo流程大致如下
在内存更新数据后,会把更新后的记录写入到 redo log buffer 中。
| 第一步:InnoDB 会先把记录从硬盘读入内存 |
| 第二部:修改数据的内存拷贝 |
| 第三步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值 |
| 第四步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加写的方式 |
| 第五步:定期将内存中修改的数据刷新到磁盘中(注意注意注意,不是从redo log file刷入磁盘,而是从内存刷入磁盘,redo log file只在崩溃恢复数据时才用),如果数据库崩溃,则依据redo log buffer、redo log file进行重做,恢复数据,这才是redo log file的价值所在 |
redo是如何保证事务的持久性的呢???
答案是Force Log at Commit 机制,即当事务commit提交时,innodb引擎先将 redo log buffer 写入到 redo log file 进行持久化,待事务的commit操作完成时才算完成。这种做法也被称为 Write-Ahead Log(预先日志持久化),在持久化一个数据页之前,先将内存中相应的日志页持久化。
问题1:为何不直接将修改的数据写入磁盘,而是要write ahead log呢?
| 答案:用于崩溃恢复 |
| 详解: |
| undo日志是对原始数据的备份 |
| redo日志是对原始数据的修改 |
| |
| 原始数据的按照既定的数据结构存放在磁盘上,写入磁盘是要耗费巨大成本的,而写入redo相对容易一些,因为redo里毕竟只需要考虑存放改动的数据即可,所以内存数据写写入redo log file,然后内存数据才能写入磁盘,如此,在内存数据再写入磁盘时因为某种原因比如断电崩溃,那么还可以依据redo log file恢复数据,如下图所示。 |
问题2:如何保证每次修改的数据都能写入redo log file呢?
| |
| O_DIRECT选项是在Linux系统中的选项,使用该选项后,对文件进行直接IO操作,不经过文件系统缓存,直接写入磁盘 |
| |
| |
| redo log又称之为重做日志,因重做日志打开并没有O_DIRECT选项,所以重做日志先写入到文件系统缓存,然后才会刷入硬盘,即 |
| Redo log buffer--->os cache(文件系统缓存)--->redo log file |
| |
| 如果在刷入redo log file前断电,则会丢失文件系统缓存中数据,数据未写入redo log file, |
| 因为由内存写入redo log file在前,而由内存写入磁盘在后,所以redo log file写入失败,则数据丢失 |
| |
| |
| 答案是fsync操作 |
| 在每次将redo buffer写入os cache文件系统缓存后,InnoDB存储引擎都需要调用一次 fsync操作,保证立即由os cache文件系统缓存写入redo log file |
| |
| fsync是一种系统调用操作,其fsync的效率取决于磁盘的性能,因此磁盘的性能也影响了事务提交的性能,也就是数据库的性能。 |
问题3:脏页何时刷入磁盘呢?
| |
| Buffer Pool 中更新的数据未刷新到磁盘中,该内存页我们称之为脏页。最终脏页的数据会刷新到磁盘中,将磁盘中的数据覆盖,这个过程与 redo log 不一定有关系。 |
| |
| |
| redo log 日志满了的情况下,会主动触发脏页刷新到磁盘 |
问题4:脏页只在redo log满的情况下才会刷入磁盘吗?
| 答案:no,以下几种情况同样会触发脏页的刷新 |
| |
| - 1、系统内存不足时,需要将一部分数据页淘汰掉,如果淘汰的是脏页,需要先将脏页同步到磁盘; |
| - 2、MySQL 认为空闲的时间,这种情况没有性能问题; |
| - 3、MySQL 正常关闭之前,会把所有的脏页刷入到磁盘,这种情况也没有性能问题。 |
问题5:脏页刷入会带来性能问题吗?