事务的隔离性由锁机制实现。
而事务的原子性、一致性和持久性由事物的redo日志和undo日志来保证。
- redo日志(重做日志):提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性(如在内存中修改还没来得及写入磁盘中,此时系统宕机,就需要redo日志来保证修改写入到磁盘中)
- undo日志(回滚日志):回滚行记录到某个特定版本,用来保证事务的原子性、一致性
undo日志不是redo日志的逆过程,redo和undo都可以是看作是恢复操作:
- redo日志:是存储引擎层生成的日志,记录的是物理级别上的页修改操作,主要为了保证数据的可靠性
- undo日志:是存储引擎层生成的日志,记录的是逻辑操作日志,如对某个表进行了INSERT操作,那么undo日志就记录一条与之相反的DELETE操作。主要用于回滚和一致性非锁定读
redo日志
InnoDB是以页为单位来管理存储空间的。在访问页面时,需要先把磁盘上的页缓存到内存中的缓冲池中,所有的修改必须先更新缓冲池中的数据,然后缓冲区中的脏页(写入缓冲池但还没有写回磁盘的页)以一定频率被刷新到磁盘中。
为什么需要redo日志
若有这样一种场景,当事务提交后,刚写完缓冲池还没来得及将修改写回磁盘,数据库宕机了,那么这段数据就丢失了。
但是,事务包含了持久性的特点,对于一个已经提交的事务,在事务提交后即使系统发生了崩溃,这个事务对数据库中所做的更改也不能丢失。
所以,这就引入了redo日志:只需要把修改了哪些东西记录一下就好。InnoDB的事务采用了WAL(Write-Ahead Logging)技术,这种技术的思想就是先写日志(redo日志),再写磁盘,只有日志成功写入,事务才算提交成功。当服务器宕机还未刷新磁盘时,可以通过redo日志恢复,以保证事务的持久性。
redo日志概述
采用redo日志的好处:
- 降低了刷盘频率
- 占用的空间很小:只需要记录表空间ID,页号,偏移量以及需要更新的值,占用空间小刷盘快
redo日志的特点:
- redo日志是顺序写入磁盘的:使用顺序IO,效率高
- 事务执行过程中,redo日志不断记录
redo日志的组成:
- **重做日志的缓冲 (redo log buffer)**:保存在内存中,是易失的
- 重做日志文件 (redo log file) :保存在磁盘中,是持久的
redo的流程:
- 先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
- 生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
- 当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加写的方式
- 定期将内存中修改的数据刷新到磁盘中
redo日志的刷盘策略
redo log的写入并不是直接写入磁盘的,InnoDB引擎会在写redo log的时候先写redo log buffer,之后以一定的频率刷入到真正的redo log file 中。这里需要讨论redo日志的刷盘策略,以保证redo log buffer中的内容被正确的写入到磁盘中的redo日志中。
还要注意的是,redo log buffer刷盘到redo log file的过程并不是真正的刷到磁盘中去,只是刷入到文件系统缓存(page cache)中去,真正的写入会交给系统自己来决定(比如page cache足够大了)。那么对于InnoDB来说就存在一个问题,如果交给系统来同步,同样如果系统宕机,那么数据也丢失了(虽然整个系统宕机的概率还是比较小的)。
针对这种情况,InnoDB给出innodb_flush_log_at_trx_commit
参数,该参数控制提交事务时,如何将 redo log buffer 中的日志刷新到 redo log file 中,它支持三种策略:
设置为0
:表示每次事务提交时不进行刷盘操作。(系统默认master thread每隔1s进行一次redo日志的同步)设置为1
:表示每次事务提交时都将进行同步,刷盘操作(默认值)设置为2
:表示每次事务提交时都只把 redo log buffer 内容写入 page cache,不进行同步。由操作系统自己决定什么时候同步到磁盘文件。
undo日志
undo日志保证事务的原子性。在事务中更新数据的前置操作其实是写入undo日志。
undo日志的必要性
事务需要保证原子性,即事务中的操作要么全部完成,要么什么都不做。当事务执行的过程中出现了错误,需要回滚时,就需要undo日志:把事务过程中对记录的改动(插入、删除、修改)记录下来,以便进行回滚操作。
此外,undo日志会产生redo日志,undo日志的产生也会伴随着redo日志的产生,因为undo日志也需要持久性的保护。
InnoDB对undo log的管理采用段的方式,也就是回滚段(rollback segment)。每个回滚段记录了1024 个undo log segment,而在每个undo log segment段中进行 undo页 的申请。
- 在InnoDB1.1之前,只有一个回滚段,因此支持同时在线的事务限制为1024
- 从InnoDB1.1开始,支持最大128个回滚段,故其支持同时在线的事务限制提高到了128 * 1024
总结
在一次事务中进行异常数据更新时,进行一下步骤:
- 将待更新的数据从磁盘加载到内存的缓冲池Buffer Pool中
- 写入undo日志便于进行回滚
- 写入redo日志到内存中的redo log buffer中
- 提交后,redo日志从内存中写到文件系统缓存中,并最终同步到磁盘中的redo日志文件中