undo log
什么是undo log? 为什么要有undo log?
在日常的事务中,我们经常会遇到事务执行失败回滚,所谓回滚,就是将数据恢复到事务执行之前的状态。 所以,我们需要一个东西,去记录我们每次变动都修改了啥,不然我们一旦执行,数据发生修改了,我们想回滚也 不知道以前我们的数据是个啥了。而保存我们变动之前的状态的文件就是undo log,也就是说,这玩意就是给我们 反悔(rollback)用的
事务的标签 :事务ID
按照事务读写的分类
-
只读事务
- 在只读事务中,通常是不分配事务ID的,因为只读事务通常没有写操作
- 但是,如果只读事务中用到了临时表,并且对临时表进行了更改,则会分配事务ID
-
读写事务
- 读写事务不一定包含更新操作,也可能只有读操作,只有读操作的时候,是不会分配事务ID的
- 读写事务中无论是对表或者是对临时表进行了写操作,都会分配事务ID
总而言之,就是不管你是啥事务,你写了我就给你一个事务ID
事务ID是什么?
事务ID的本质就是一个全局数字变量,没次分配一个事务ID之后,这个变量就会执行一个自增。
表的隐藏列 trx_id (ROW_TRX_ID)
我们说在InnoDB引擎中,如果我们没有显示的创建主键,那么innodb会给我们隐式的创建三列
- row_id 隐藏的主键 唯一 [非必须 如果我们显示的指定了主键则不会有这个列]
- trx_id 这个就是存放事务中分配的全局事务ID的列
- roll_pointer 指向记录对应的undo log的指针
Insert 操作的 undo log 的结构
- end_of_record 本条日志结束,下条日志开始的位置
- undo_type 日志的类型
- undo no 本条undo log对应的编号
- table id 本条日志对应的table 的id
- start_of_record 本条开始,上条日志的位置
- 主键的每列占用的存储空间大小<key, value> key: 主键列的字段长度 value: 主键的值
Delete 操作的 undo log的结构
删除一条数据的步骤
我们执行删除数据的时候,分两步
- 第一步: 更改行结构中真实数据额外字段中的 delete_mask 字段 这个字段就是用来标志数据是否被删除的 同时,也会相应的更改trx_id, roll_pointer的信息 在这个阶段, 删除动作还没有执行commit
- 第二步: 执行commit, 会有其他线程将本行数据从正常的数据链表中踢掉,同时把他加入到垃圾数据的链表中 同时还要调整该数据页的一些信息比如用户记录数量 PAGE_N_RECS 等等 上面两步执行结束才是真正的将数据删除了。
针对上述删除一阶段提出的 undo log 格式
为啥是针对一阶段,因为二阶段的delete已经提交了,就没法rollback了
- end_of_record
- undo_type
- undo no
- table id
- info bits
- old trx_id
- old roll_pointer
- 各主键的空间大小
- index_col_info
- 索引列各列信息
- start_of_record
Update 操作对应的 undo log
更新情况的undo log 分两种情况
不更新主键的Update
- end_of_record
- undo_type
- undo no
- table id
- info bits
- old trx_id
- old roll_pointer
- 各主键的空间大小
- n_updated 总共有多少列被更新了
- 被更新列更新前信息
- 索引列各列信息
- index_col_info
- start_of_record
其实不更新主键的时候也分两种情况,
-
第一种是更新的信息长度大小都不变 (字段长度以及真实数据长度都不变)
- 这种情况下是直接更新数据的
-
第二种情况是数据长度发生变化
- 这种情况是先执行把原行删除再插入新行的操作
更新主键的Update
设计更新主键的时候,undo 日志格式和我们上面的基本不变,唯一变化的是undo 日志会从一条变成两条 因为在聚簇索引中,所有的数据是根据主键大小排序存放的,如果主键发生了变化,意味着数据要发生移动