首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >面试题86:DELETE操作对应的undo日志

面试题86:DELETE操作对应的undo日志

作者头像
爪哇缪斯
发布2023-05-10 09:41:51
发布2023-05-10 09:41:51
4600
举报
文章被收录于专栏:爪哇缪斯爪哇缪斯
  • 正常记录链表 记录的头信息中的next_record属性组成一个单向链表,我们把这个链表称为正常记录链表。
  • 垃圾链表 被删除的记录其实也会根据记录头信息中的next_record属性组成一个链表,只不过这个链表中的记录所占用的存储空间可以被重新利用,所以也称这个链表为垃圾链表。
  • PAGE_FREE的作用是什么? Page Header部分中有一个名为PAGE_FREE的属性,它指向由被删除记录组成的垃圾链表中的头节点。每删除一条记录,则该记录都会插入到垃圾链表的头节点处。
  • 举例,有3条正常记录和2条被删除记录,他们在页中的记录分布情况如下所示:

【注】在垃圾链表中,这些记录占用的存储空间可以被重新利用。

  • 如果要删除一条记录,则需要两个步骤
  • 第一步:delete mark阶段 仅仅将记录的deleted_flag标识位设置为1,但是这条记录并没有加入到垃圾链表中。也就是说,这条记录即不是正常记录,也不是已删除记录。在删除语句所在的事务提交之前,被删除的记录一直都处于这种中间状态(其实主要是为了实现MVCC的功能才这样处理的)。 如下所示:
  • 第二步:purge阶段 当该删除语句所在的事务提交后,会有专门的线程来把该记录从正常记录链表中移除,并加入到垃圾链表中作为头节点。 如下所示:

【关于其它垃圾链的重用空间的知识点补充介绍】

  • PAGE_GARBAGE是做什么的? Page Header部分有一个名为PAGE_GARBAGE的属性。该属性记录着当前页面中可重用存储空间占用的总字节数。每当有已删除记录加入到垃圾链表后,都会把这个PAGE_GARBAGE属性的值加上已删除记录占用的存储空间大小。
  • 如何重用垃圾链表的存储空间? PAGE_FREE指向垃圾链表的头节点,每当新插入数据的时候:
  • 首先:判断垃圾链表头节点记录的存储空间是否足够容纳这条新插入的记录。如果可以容纳则直接重用这条已删除记录的存储空间。
  • 其次:如果不能容纳,则直接向页面申请新的空间来存储这条记录。(是的,你没看错!并不会尝试遍历垃圾链表,以找到可以容纳新记录的节点)
  • 如果新插入的那条记录记录小于重用的记录空间,那么会有一部分空间用不到,怎么处理呢?直接浪费掉吗? 这种情况会频繁发生,也就会随着记录越插越多而产生越来越多的空间碎片。只有当页面块满的时候,如果再插入一条新记录,无法分配一条完整的记录空间时,会先查看PAGE_GARBAGE的空间和剩余空间相加是否可以容纳这条新的记录,如果可以,InnoDB则会尝试重新组织页内的记录。即:先开辟一个临时页面,把原页面内的记录依次挨着插入一遍到临时页,之后,再把临时页的内容复制到本页面,这样就可以把那些碎片空间都释放出来了。但是该操作比较耗费性能。
  • 由于一旦事务提交,我们也就不需要再回滚这个事务了,所以在设计undo日志时,只需要考虑delete mark这个阶段所做的影响进行回滚就可以了。
  • TRX_UNDO_DEL_MARK_REC类型的undo日志结构如下所示:
  • info bits

记录头信息的前4个比特的值。

  • trx_id

旧记录的trx_id值。

  • roll_pointer

旧记录的roll_pointer值。

  • len of index_col_info

也就是下边的【索引列各列信息】部分和本部分占用的存储空间总和。

  • 索引列各列信息 <pos, len, value>列表

凡是被索引的列的各列信息。

  • 为什么TRX_UNDO_DEL_MARK_REC类型的undo日志保存旧记录的trx_id值和roll_pointer值? 保存旧记录的trx_id值——为了采用事务id作为版本号,记录每个undo日志所对应的版本是多少。 保存旧记录的roll_pointer值——可以通过undo日志的roll_pointer属性找到上一次对该记录进行改动时产生的undo日志,因此可以将日志串成链表。这个链表就是版本链。
  • 我们模拟一下,新增一条记录,然后再删除这条记录的完整操作过程,如下所示:
  • 演示删除操作生成undo日志。

我们先插入两条记录:

代码语言:javascript
复制
BEGIN; # 显示开启一个事务,假设该事务的事务id为100
# 插入两条记录
INSERT INTO tb_user(id, name, city) VALUES(1, 'muse','北京市'), (2, 'bob','上海市');
# 删除一条记录DELETE FROM tb_user WHERE id = 1;

其中需要说明两个字段:

  • 索引列各列信息 <pos, len, value>列表

<0, 4, 1>:

由于id列是主键,所以pos=0;

由于id列的类型是INT,所以len=4;

由于id=1,所以value=1;

<3, 4, 'muse'>:

由于name列是二级索引,它排在id列、trx_id列、roll_pointer列之后,所以pos=3;

由于id列的类型是INT,所以len=4;

由于name='muse',所以value='muse';

  • len of index_col_info pos使用1字节来存储。 len使用1字节来存储。 value根据具体值,来判断。比如:id=1,主键是INT占4个字节,所以value使用4字节存储。name='muse',VARCHAR类型,所以value用4字节存储。 len of index_col_info本身占2个字节。

综上所述,<0, 4, 1><3, 4, 'muse'>占用空间等于:(1+1+4)+(1+1+4) =12,如下图所示:

最后,再加上len of index_col_info属性本身占2个字节,所以总共14字节。即:len of index_col_info=14。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-10-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爪哇缪斯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档