undo log
redo 重做日志
在对buffer pool里的缓存页执行增删查改操作的时候,必须要写对应的redo log记录下来你做了哪些修改。
如下图所示,redo log都是先进入redo log buffer中的一个block,然后事务提交的时候就会刷入磁盘文件里去。
这样万一要是你提交事务了,结果事务修改的缓存页还没来得及刷入磁盘上的数据文件,此时MySQL宕机或者关闭了,那么buffer pool里被事务修改过的数据就全部都丢失了。
但是只要有redo log,重启MySQL之后完全可以把那些修改了缓存页,但是缓存页还没来得及刷入磁盘的事务所对应的redo log都加载出来,在buffer pool的缓存页里重做以便,就可以保证事务提交之后,修改的数据绝对不会丢
undo回滚日志
这种日志要应对的场景就是事务回滚的场景。
- 比如现在我们一个事务里要执行一些增删改操作,那么必须是先把对应的数据页从磁盘加载出来放在buffer pool的缓存页里,然后在缓存页中执行增删改操作,同时记录redo log日志。
-
但是问题来了,万一要是一个事务里的一通增删改操作执行到了一半,结果就回滚事务了呢?
-
比如一个事务有4个增删改操作,结果目前为止已经执行了2个增删改SQL了,已经更新了一些buffer pool里的数据了,但是还有2个增删改SQL的逻辑还没有执行,此时事务要回滚了怎么办?
-
这个时候就很尴尬了,如果你要回滚事务的话,那么必须要把已经在buffer pool的缓存页里执行的增删改操作给回滚了。
-
但是怎么回滚呢?毕竟无论是插入,还是更新,还是删除,该做的都已经做了
-
所以在执行事务的时候,必须引入另外一种日志,就是undo log回滚日志。
这个回滚日志,记录的东西非常简单:
- 比如如果在缓存页里执行了一个insert语句,那么此时undo log必须记录了插入数据的主键ID,回滚的时候就可以从缓存页里把这条数据给删除了;
- 如果在缓存页里执行了一个delete语句,那么undo log必须记录下来被删除的数据,回滚的时候就得重新插入一条数据
- 如果在缓存页里执行了一个update语句,那么起码要把更新之前的那个值记录下来,回滚的时候重新update一下,把之前更新的旧值更新回去。
- 如果在缓存页里执行了一个select语句,因为没有改变buffer pool,因此不需要任何undo log。
如下图,在事务执行期间,除了写redo log日志还必须写undo log日志。通过undo log日志可以回滚事务。
INSRET语句的undo log回滚日志长什么样?
INSERT语句的undo log的类型是TRX_UNDO_INSERT_REC,这个undo log里包含了以下一些东西:
- 这条日志的开始位置
- 主键的各列长度和值:
- 你插入一条数据,必然会有一个主键
- 如果自己指定了一个主键,那么:
- 可能这个主键就是一个列,比如id之类的,
- 也可能是多个列组成的一个主键,比如“id+name+type”三个字段组成的一个联合主键
- 所以这个主键的各列长度和值,意思就是你插如的这条数据的主键的每个列,它的长度是多少,具体的值是多少。
- 如果你没有设置主键,MySQL自己会弄一个row_id作为隐藏字段做主键
- 表id:
- 插入一条数据必然是往一个表里面插入数据的,那当然有一个表id,记录下来是在哪个表里面插入的数据了
- undo log日志编号:
- 每个undo log都有自己的编号
- 而一个事务里会有多个SQL语句,就会有多个undo log日志,在每个事务的undo log日志的编号都是从0开始的,然后依次递增
- undo log日志类型:
- insert语句的undo log日志类型为
TRX_UNDO_INSERT_REC
- insert语句的undo log日志类型为
- 这条日志的结束位置
有了这条日志之后,剩下的事儿就好办了:
- 万一要是你现在在buffer pool的一个缓存页里插入了一条数据了,执行了insert语句,然后你写了一条上面的那种undo log,现在事务要是回滚了,你直接就把这条insert语句的undo log拿出来。
- 然后在undo log里就知道在哪个表里插入的数据,主键是什么,直接定位到那个表和主键对应的缓存页,从里面删除掉之前insert语句插入进去的数据就可以了,这样就可以实现事务回滚的效果了!