MySQL重做日志redo log
MySQL重做日志redo log
在事务的ACID特性中,原子性(A)、一致性(C)、持久性(D)由undo log和redo log实现,隔离性(I)由锁+MVCC实现
undo log:事务还没有commit,中途执行异常,可以使用undo log把数据恢复到事务执行前的状态,确保事务的原子性redo log:事务commit成功,,此时若发生异常,就要使用redo log重新执行这一事务的SQL,确保事务的持久性(只要事务commit成功,不管发生什么异常事件,只要下一次MySQL服务正常进行,那上一次commit的数据一定要恢复回来)
一、redo log概念
redo log:被称为物理日志,用于记录事务操作的变化,确保事务的持久性。,无论事务是否commit都会记录,若异常发生,下一次mysqld再启动时,会使用redo log将数据重新写入磁盘,确保事务的持久性。
undo log:被称为逻辑日志,存储的是具体的相应的SQL语句。
redo log默认放在/var/lib/mysql
下

redo log是在事务begin时就开始记录(并不是事务commit时才记录,因为整个事务做的操作可能很多,如果在commit的时候才写redo log,此时一旦发生异常,redo log还没写,这就太晚了,无法确保事务的持久性),不管事务是否提交都会记录下来,在异常发生时(如数据持久化过程中掉电),InnoDB会使用redo log恢复到掉电前的时刻,保证数据的完整性
innodb_log_buffer_size
默认是16M,就是redo log缓冲区的大小,它随着事务开始,就开始写redo log,如果事务比较大,为了避免事务执行过程中花费过多磁盘IO,可以设置比较大的redo log缓存,节省磁盘IO。往磁盘上刷是有刷新的时机,达到时机就花费磁盘IO,如果buffer比较大,会更慢的达到刷新的时机,效率更高。

**InnoDB修改操作数据,不是直接修改磁盘上的数据,实际只是修改Buffer Pool中的数据。**InnoDB总是先把Buffer Pool中的数据改变记录到redo log中,用来进行崩溃后的数据恢复。 优先记录redo log,然后会有一个独立的线程找时机慢慢的将Buffer Pool中的脏数据刷新到磁盘上(异步过程)
innodb_log_group_home_dir
指定的目录下的两个文件:ib_logfile0,ib_logfile1
,该文件被称作重做日志
buffer pool缓存池: 可存放索引缓存、数据缓存等,可加速读写,直接操作数据页,,有专门的线程去做把buffer pool中的dirty page写入磁盘
buffer pool大致结构如图所示:

事务读取,修改都是优先操作缓存池中的数据。在实际项目中,mysqld会单独的跑在一个机器上,可以分配大量的内存专门作为InnoDB的buffer pool,加快CRUD
buffer pool默认大小为128M(MySQL 5.7和8.0一样)

二、缓存、磁盘结构

当事务commit的时候,在关系图上的操作就是,写成功的话,在磁盘上的redo log会记录状态为commit,如果没有写成功或者写完,则记录状态为prepare
log在写入磁盘的过程中也有可能发生异常,断电等问题,导致在写redo log的时候没有写完(这相当于事务没有commit成功),此时MySQL下次再恢复的时候就没有必要考虑这个事务的完整性,因为状态并不是commit,都写入磁盘上才表示redo log写成功,状态才变成commit,状态变成commit后需要维护事务的ACID特性。
是不是commit的时候,buffer pool里面的脏数据(数据有被修改)同步被写入磁盘?
并不需要等commit的时候才开始。事务可能修改的数据量比较大,而缓存容量有限,对于buffer pool缓存的数据,会有专门的线程在合适的时间,往磁盘上去刷新,如果出现掉电,下一次MySQL启动后,会根据redo log里面记录的数据,对数据进行恢复。所以当事务commit后,最重要的是redo log要写成功
undo log本身也是记录在redo log中
undo log支持事务回滚,也不是一瞬间就能完整,font>,回滚过程也会出现异常,下次服务重启时,需要使用undo log重新回滚,所以undo log要记录在redo log里面。事务commit成功或者rollback成功,对于底层,都是成功的把操作写到磁盘上的redo log里面
什么是真正的事务commit成功?
不是把数据全部刷到磁盘,而是把记录事务完整操作的redo log从log buffer写入磁盘,再把被修改数据的状态置为commit才算是实现了事务commit成功。此时虽然数据还在buffer pool,但只要我们的redo log保存完整,数据就可以恢复,会有专门的线程去负责把buffer pool里的数据写入磁盘
一个事务commit成功通常涉及以下几个关键步骤:
- Redo Log的写入:在事务执行过程中,所有对数据的修改操作都会被记录在redo log中。这些redo log首先被写入log buffer,然后在事务提交时,会被刷新(flush)到磁盘上的redo log文件中。这一步是确保事务持久性的关键,因为如果系统崩溃,redo log可以用来恢复数据到事务提交时的状态。
- 数据页状态的更改:在InnoDB存储引擎中(以MySQL为例),数据通常保存在buffer pool中,这是一个内存缓存,用于加速数据的读取和写入。当事务提交时,被修改的数据页的状态会被标记为“commit”。这意味着这些页上的修改是持久的,即使它们还没有被刷新到磁盘上的数据文件中。
- 后台的I/O操作:尽管数据页可能还在buffer pool中,但后台的I/O线程会异步地将这些页刷新到磁盘。这个过程是异步的,它不会影响事务的提交。即使刷新过程中发生了错误,由于有redo log的存在,数据仍然可以恢复。
什么叫事务commit成功?(和上面的问题一样)
事务执行commit命令后,mysqld将记录了数据修改的Log Buffer写入磁盘上的redo log,然后将这部分数据的状态修改为commit,写完redo log后才算是事务提交成功,MySQL Server需要保持commit状态的数据的持久性。如果写redo log失败,数据的状态还是prepare,尽管事务执行了commit命令,这依然不算commit成功
事务进行操作的时候,永远是先写log buffer,然后才是写buffer pool;事务成功commit,就是要保证redo log完整记录到磁盘上
至于表数据的更改,buffer pool的脏数据页是不是刷新到磁盘上,我们根本不用担心,只要redo log完整的写到磁盘上,我们可以随时通过redo log重做日志来恢复事务成功commit的数据状态(数据库最重要的是日志,而不是数据)