数据库事务具有四个核心属性:原子性、一致性、隔离性、持久性。
一个事务要修改多张表的多条记录,多条记录分布在不同的磁盘Page里面,对应到磁盘的不同位置,如果每个事务都要直接写磁盘,一次事务提交就要有多次磁盘的随机IO,性能达不到要求。因此,先在内存中进行事务提交,然后再通过后台线程异步地把内存中的数据写入磁盘。为了避免内存中的数据没有来得及刷盘导致数据丢失,通过写Write-Ahead Log,将内存中的数据顺序的在日志尾部Append,避免磁盘的随机IO,这个Write-Ahead Log的日志,在InnoDB中,称为Redo日志。
Redo Log日志本身也是异步的,事务提交后,Redo Log先写入内存中的Redo Log Buffer中,然后异步地刷到磁盘的Redo Log中。可以通过InnoDB的innodb_flush_log_at_trx_commit控制Redo Log的刷盘策略。
Redo Log属于物理逻辑日志,先以Page为单位记录日志,再在每个Page里采用逻辑记法。通过记录page日志可能知道哪些page写成功或失败了,page里面采用逻辑记日志可以减少日志量。
Page的刷盘时由于IO写入不一定具有原子性(需要硬件支持),如果写入失败,无法通过redo log进行数据恢复,因此,在将page刷盘前,先写入到一个临时的磁盘位置,写入成功后再拷贝到目标磁盘位置,如果目标磁盘写入失败,可以通过这个备份进行恢复,这个备份日志就叫Undo Log。
不同事务的日志在Redo Log中是交叉存在的,这意味着未提交的事务也在Redo Log中。因此,当崩溃恢复的时候,会把Redo Log全部重放一遍,提交的事务和未提交的事务被重放了,从而让数据库“原封不动”地回到宕机前的状态(在ARIES算法中称为Repeating History)。重放完成后,需要再把宕机前未完成的事务通过ARIES的Checkpoint机制找出来,然后逐一地用Undo Log回滚。回滚通过Checkpoint记录的“活跃事务表”+每个事务日志中的开始/结束标记+Undo Log实现。
Undo Log维护了数据从旧到新的每个版本,各个版本之间通过链表串连,因为有了多版本,从而实现了事务的隔离性。
结论:
Undo Log + Redo Log:实现了事务的原子性和持久性;
MVCC+锁:实现了事务的隔离性和并发性。