事务日志简介
事务有四种特性:原子性、一致性、隔离性、持久性,详情请看《mysql 事务的基础知识》。其中隔离性由锁机制实现,原子性、一致性由 undo 日志(undo log 称为回滚日志,回滚记录到某个特定版本)来保证,持久性则是由 redo 日志(redo log 称为重做日志,提供写操作,恢复提交事务修改的页操作)来保证。
有的人认为 undo 是 redo 的逆过程,其实不然。redo 和 undo 都是存储引擎层(InnoDB)生成的日志,都可以视为一种恢复操作,但 redo log 记录的是物理级别
上的页修改操作,undo log 记录的是逻辑操作
日志,主要用于事务的回滚(undo log 记录的是每个修改操作的逆操作)和一致性非锁定读物
(undo log 回滚行记录到某种特定的版本——MVCC,即多版本并发控制)。
redo 日志
InnoDB 存储引擎是以页为单位来管理存储空间的。在真正访问页之前,需要把磁盘上的页缓存到内存中的 Buffer Pool 之后才可以访问。所有的更改都必须先更新缓冲池中的数据,然后缓冲池中的脏页会以一定的频率被刷入磁盘(checkPoint机制),通过缓冲池来优化 CPU 和磁盘之间的鸿沟,这样就可以保证整体性能不会下降太快。
InnoDB 引擎的事务正是采用了 WAL 技术(Write-Ahead Logging),这种技术的思想就是先写日志,在写磁盘,只有日志写入成功,才算事务提交成功,这里的日志就是 redo log。当发生宕机且数据未刷到磁盘的时候,可以通过 redo log 来恢复,保证一致性。
redo 日志的特点和好处
使用 redo 日志的好处:降低了刷盘频率、redo 日志占用空间非常小。
特点:
- redo 日志是顺序写入磁盘的:在执行事务的过程中,每执行一条语句,就可能产生若干条 redo 日志,这些日志是按照产生的顺序写入磁盘的,也就是顺序 IO,效率比随机 IO 快。
- 事务执行过程中,redo log 不断记录:redo log 跟 bin log 的区别是 redo log 是存储引擎产生的,而 bin log 是数据库层产生的。
redo 的组成
redo log 可以简单分为两个部分:
- 重做日志缓冲(redo log buffer):保存在内存中,是易失的,默认 16M,最大 4096M,最小 1M。在服务器启动时就像操作系统申请了一大片连续的内存空间,称为 redo 日志缓冲区。这篇内存空间被划分成若干个连续的 redo log block,一个 redo log block 占用 512 字节。
- 重做日志文件(redo log file):保存在硬盘中,是持久的。默认有两个文件(由
innodb_log_files_in_group
的值决定) ib_logfile0 和 ib_logfile1。
redo 的整体流程
- 先将原始数据从磁盘中读到内存中,修改数据的内存拷贝。
- 生成一条重做日志并写入 redo log buffer。记录的是数据被修改后的值
- 当事务 commit 时,将 redo log buffer 中的内容刷新到 redo log file。对 redo log file 采用追加的方式
- 定期将内存中修改的数据刷新到磁盘中
redo log 的刷盘策略
redo log buffer 刷盘到 redo log file 的过程中并不是真正的刷到磁盘中去,只是刷入到文件系统缓存(page cache)中去(这是现代操作系统为了提高文件写入效率做的一个优化),真正的写入会交给操作系统自己决定。那么对于 InnoDB 来说就存在一个问题,如果交给系统来同步,如果系统宕机,那么数据也就丢失了。
针对这种情况,InnoDB 给出 innodb_flush_log_at_trx_commit
参数,该参数控制 commit 提交事务时,如果将 redo log buffer 中的日志刷新到 redo log file 中。它支持三种策略:
- 设置为0:表时每次事务提交时不进行输盘操作(系统默认 master thread 每隔 1 秒进行一次重做日志的同步)
- 设置为1:该值为默认值,表示每次事务提交时都将进行同步,主动刷盘操作
- 设置为2:表示每次事务提交时都只把 redo log buffer 内容写入 page cache,不进行同步。由 OS 自己决定什么时候同步到磁盘文件。
undo 日志
undo log 是事务原子性的保证。在事务中更新数据的前置操作其实是要先写入一个 undo log。
事务需要保证原子性,也就是事务中的操作要么全部完成,要么什么也不做。因此,undo log 的一个作用就是用来回滚数据,另一个作用是 MVCC,即在 InnoDB 存储引擎中 MVCC 的实现是通过 undo 日志来 完成。当用户读取一行记录时,若该记录已经被其他事务占用,当前事务就可以通过 undo 读取之前的版本信息,一次实现非锁定读取。
undo 日志的存储结构
InnoDB 对 undo log 的管理采用段的方式,也就是回滚段(rollback segment),存储于共享表空间 ibdata 中。每个回滚段记录了 1024 个 undo log segment,而每个 undo log segment 中进行 undo 页的申请。
- 在 InnoDB1.1 版本之前(不包括 1.1 版本),只有 rollback segment,因此支持同时在写的事务限制为 1024。
- 从 1.1 版本开始 InnoDB 最大支持 128 个 rollback segment,故其支持同时在写的事务限制提高到了 128 * 1024 个。
从 InnoDB 1.2 开始,可通过参数对 rollback segment 做进一步的设置。这些参数包括:
- innodb_undo_directory:设置 rollback segment 文件所在的路径。这意味着 rollback segment 可以存放在共享表空间外的位置,即可以设置为独立表空间。默认值为“./”,表示当前 InnoDB 存储引擎的目录。
- innodb_undo_logs:设置 rollback segment 的个数,默认值为 128.在 InnoDB 1.2 中,该参数用来替换之前版本的参数 innodb_rollback_segments。
- innodb_undo_tablespaces:innodb_undo_tablespaces:设置成 rollback segment 文件的数量,这样 rollback segment 可以较为平均的分布在多个文件中。设置该参数后,会在路径 innodb_undo_directory 看到 undo 为前缀的文件,该文件就代表 rollback segment 文件。
undo 日志的类型
在 InnoDB 存储引擎中,undo log 分为:
- insert undo log:指在 insert 操作中产生的 undo log。因为 insert 操作的记录只对事务本身可见,对其他事务不可见(事务隔离性的要求),故 undo log 可以在事务提交后直接删除。不需要进行 purge 操作。
- update undo log:记录的是 delete 和 update 操作产生的 undo log。该 undo log 可能需要提供 MVCC 机制,因此不能在事务提交时就删除。提交时放入 undo log 链表,等待 purge 线程进行最后的删除。
回滚段中的数据分类:
- 未提交的回滚数据(uncommitted undo information):该数据所关联的事务并未提交,用于实现读一致性,所以该数据不能被其他事务的数据覆盖。
- 已提交但未过期的回滚数据(committed undo information):改数据关联的事务已经提交,但仍受到 undo retention 参数的保持时间的影响。
- 事务已经提交并过期的数据(expired undo information):事务已经提交,而且数据保存时间已经超过 undo retention 参数指定的时间,属于已经过期的数据。当回滚段满了之后,会优先覆盖“事务已经提交并过期的数据”。