事务回滚的需求
我们说过事务需要保证原子性,也就是事务中的操作要么全部完成,要么什么也不做。但是偏偏有时候事务执行到一半会出现一些情况,比如:
情况一:事务执行过程中可能遇到各种错误,比如服务器本身的错误,操作系统错误,甚至是突然断电导致的错误。
情况二:程序员可以在事务执行过程中手动输入ROLLBACK语句结束当前的事务的执行。
这两种情况都会导致事务执行到一半就结束,但是事务执行过程中可能已经修改了很多东西,为了保证事务的原子性,我们需要把东西改回原先的样子,这个过程就称之为回滚(英文名:rollback),这样就可以造成一个假象:这个事务看起来什么都没做,所以符合原子性要求。
从上面的描述中我们已经能隐约感觉到,每当我们要对一条记录做改动时(这里的改动可以指INSERT、DELETE、UPDATE),都需要留一手 —— 把回滚时所需的东西都给记下来。比方说:
你插入一条记录时,至少要把这条记录的主键值记下来,之后回滚的时候只需要把这个主键值对应的记录删掉就好了。
你删除了一条记录,至少要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。
你修改了一条记录,至少要把修改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。
设计数据库的大佬把这些为了回滚而记录的这些东东称之为撤销日志,英文名为undo log,我们也可以土洋结合,称之为undo日志。这里需要注意的一点是,由于查询操作(SELECT)并不会修改任何用户记录,所以在查询操作执行时,并不需要记录相应的undo日志。
1. Insert 操作对应的 undo 日志
对应的undo 日志类型是: TRX_UNDO_INSERT_REC 乐观插入和悲观插入,
2. Delete 操作对应的 undo 日志:
对应的undo 日志类型是: TRX_UNDO_DEL_MARK_REC
3. Update 操作对应的 undo 日志
不更新主键
情况1:原地更新(in-place update):对于被更新的每个列来说,如果更新后的列和更新前的列占用的存储空间都一样大,那么就可以进行就地更新,也就是直接在原记录的基础上修改对应列的值
情况2:先删除旧记录,再插入新记录
针对UPDATE不更新主键的情况(包括上面所说的就地更新和先删除旧记录再插入新记录),设计InnoDB的大佬们设计了一种类型为 TRX_UNDO_UPD_EXIST_REC 的undo日志
更新主键
步骤1: 将旧记录进行 delete mark 操作, 对应 TRX_UNDO_DEL_MARK_REC 的undo日子
步骤2: 根据更新后各列的值创建一条新记录,并将其插入到聚簇索引中,产生 TRX_UNDO_INSERT_REC日志
也就是说每对一条记录的主键值做改动时,会记录2条undo日志。这些日志的格式我们上面都介绍过了,就不赘述了。