redo log
redo log 介绍
redo log(重做日志)是 InnoDB 存储引擎独有的,它用来保证 MySQL 的崩溃恢复能力。
当 MySQL 实例崩溃了,重启的时候,InnoDB 存储引擎就会使用 redo log 恢复数据,保证数据的持久性与完整性。
redo log 采用 WAL(Write-Ahead Logging)技术,即先写日志,再写磁盘。
当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log 里,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,这个更新往往是在系统比较空闲的时候做。
redo log 是物理日志,记录的是“在某个数据页上做了什么修改”,即修改后的结果;
redo log 文件
InnoDB 的 redo log 是固定大小的,比如可以配置为一组4个文件,每个文件的大小是1GB,即 redo log 总共就可以记录4GB的操作。从头开始写,写到末尾就又回到开头循环写。文件中包含两个指针 write pos 、checkpoint。
如下图所示:
redo log 是循环写的,空间固定会用完;
write pos 用来当前记录的位置,一边写一边后移,写到末尾后就回到文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。
write pos 和 checkpoint之间是 redo log 空着的部分,可以用来记录新的操作。如果 write pos 追上了 checkpoint,这时就表示 redo log 满了,这时候不能再执行新的更新,要先停下来先擦掉一些记录,把 checkpoint 推进一下。清除出一些空余空间,才可以继续写。
redo log 刷盘机制
事务在执行过程中,生成的 redo log 是要先写到 redo log buffer 的,然后 wirte 到 page cache,最后持久化到磁盘。
redo log 的刷盘策略与参数 innodb_flush_log_at_trx_commit 有关,可选择三种策略:
- 0 :设置为 0 的时候,表示每次事务提交时不进行刷盘操作,每次事务提交时都只是把 redo log 留在 redo log buffer 中;
- 1 :设置为 1 的时候,表示每次事务提交时都将redo log直接持久化到磁盘;(默认值)
- 2 :设置为 2 的时候,表示每次事务提交时都只把 redo log buffer 内容写入 page cache
innodb_flush_log_at_trx_commit 参数默认为 1
另外,InnoDB 存储引擎有一个后台线程,每隔 1 秒,就会把 redo log buffer 中的内容写到 page cache,然后调用 fsync 刷盘。
还有一种情况,当 redo log buffer 占用的空间即将达到 innodb_log_buffer_size 一半的时候,后台线程会主动刷盘。
另一种是,并行的事务提交的时候,顺带将这个事务的 redo log buffer 持久化到磁盘。假设一个事务 A 执行到一半,已经写了一些 redo log 到 redo log buffer 中,这时候有另外一个线程的事务 B 提交,如果 innodb_flush_log_at_trx_commit 设置的是 1,那么按照这个参数的逻辑,事务 B 要把 redo log buffer 里的日志全部持久化到磁盘。这时候,就会带上事务 A 在 redo log buffer 里的日志一起持久化到磁盘。
bin log
bin log 介绍
binlog 是 Server层 的日志,所有引擎都可以使用。
binlog是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1 ”。
binlog是追加写入的。“追加写”是指binlog文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
bin log 格式
binlog 日志有三种格式,可以通过 binlog_format 参数指定。
- statement:记录的内容是 SQL 语句原文
- row:记录的内容不再是简单的 SQL 语句了,还包含操作的具体数据,比如语句中涉及了当前时间的情况,update t set time=now() where id=1,row 格式记录的内容看不到详细信息,要通过 mysqlbinlog 工具解析出来。但是这种格式需要更大的容量来记录,比较占用空间,恢复与同步时会更消耗 IO 资源,影响执行速度。
- mixed:顾名思义,混合方式,MySQL 会判断这条 SQL 语句是否可能引起数据不一致,如果是,就用 row 格式,否则就用 statement 格式。
bin log 刷盘机制
事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 中,并清空 binlog cache。
系统给 binlog cache 分配了一片内存,每个线程一个,参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。
这就涉及到两个操作:
- write,指的就是指把日志写入到文件系统的page cache,并没有把数据持久化到磁盘,所以速度比较快。
- fsync,才是将数据持久化到磁盘的操作。一般情况下,我们认为fsync才占磁盘的IOPS。
write 和 fsync 的时机,是由参数 sync_binlog 控制的:
- sync_binlog = 0 的时候,表示每次提交事务都只 write,不 fsync,由系统自行判断什么时候执行 fsync,如果主机崩溃,会丢失 page cache 里的 binlog 日志;
- sync_binlog = 1 的时候,表示每次提交事务都会执行 fsync;
- sync_binlog = N(N>1) 的时候,表示每次提交事务都write,但累积N个事务后才fsync。如果主机崩溃,会丢失最近N个事务的binlog日志。
undo log
如果想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行回滚,恢复机制就是通过 undo log 实现的,所有事务进行的修改都会先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常的话,直接利用 undo log 中的信息将数据回滚到修改之前的样子即可。并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚将之前未完成的事务。
undo log 也是逻辑日志。
undo log 的另一大应用就是 MVCC,MVCC 的实现依赖于:隐藏字段、Read View、undo log。在内部实现中,InnoDB 通过数据行的 DB_TRX_ID 和 Read View 来判断数据的可见性,如不可见,则通过数据行的 DB_ROLL_PTR 找到 undo log 中的历史版本。
参考资料
《MySQL 45 讲》
《JavaGuide》
《MySQL 必知必会》