MySQL记录删除后竟能按中间被删除的主键加回去,磁盘空间被重用!——底层揭秘MySQL行格式记录头信息
说在前面——本篇也是我读书总结笔记,因为是讲底层原理,我个人认为本文难度是相当高的,可能需要一定的基础。
我将文中的图片包含的计算原理全都演算了一遍,因为书本没有过程详细计算,欢迎大家阅读和仔细推敲。
如果是大忙人,也可以跳过计算,看懂过程和大致原理即可。
文章目录
- 1.记录头信息有什么用?
- 2.记录在页中的存储结构
- 3.记录头信息的底层原理和计算(show time,难度搞起来)
- 4.当记录被删除,页中记录存储结构如何变化?
- 5.当删除的记录再次被插入,页中记录存储结构如何变化?
1.记录头信息有什么用?
记录头信息里面有很多属性,最容易理解的就是next_record
指针,单链表都会有next
指针,这样才会找得到下一个结点,这对于页中的每条记录也是一样,上一条记录需要知道下一条记录在哪里。
上一篇说到了innodb
行格式,重点讲了一下dynamic
行格式,知道一条记录实际存储如下图。没办法,说到底层原理如果不看上一篇文章是不可能完全理解的,耶稣来了也没法一篇说明白,见这里MySQL的varchar水真的太深了——InnoDB记录存储结构,必须记住下图的上面行格式部分,每条记录不仅是记录的真实数据,还有记录的额外信息。
如果你目前只是为了开发学习本部分知识,那么可以直接跳到本篇第4、5节。传送–>
在utf8mb4
字符集中,能用0~4
字节表示一个字符,像varchar
这种变长类型和char
这种定长类型实际占用的字节数都会被记录到变长字段列表。
如果字段没有被NOT NULL
限定,那么就允许为NULL
,该列就会有NULL值列表。
关于记录头信息,下面这个表先列出来,往后面看的时候不理解时可以返回查看这个表,方便理解。
名称 | 大小(单位:bit) | 描述 |
---|---|---|
预留位1 | 1 | 没有使用 |
预留位2 | 1 | 没有使用 |
delete_mask | 1 | 标记该记录是否被删除 |
min_rec_mask | 1 | B+树的每层非叶子节点中的最小记录都会添加该标记 |
n_owned | 4 | 表示当前记录拥有的记录数 |
heap_no | 13 | 表示当前记录在记录堆的位置信息 |
record_type | 3 |
表示当前记录的类型,0表示普通记录,1表示B+树非叶节点记录,2表示Infimum 记录,3表示Supremum 记录
|
next_record | 16 | 表示本条记录真实数据部分到下一条记录真实数据的距离 |
从表中所说可以看到,记录头信息一共是40bit
就是5
个字节
2.记录在页中的存储结构
页是innodb
管理存储空间的基本单位,一个页的大小默认是16KB
,插入的记录会按照指定的行格式(默认dynamic
)存储到User Records
部分。但是在一开始生成页的时候,其实并没有User Records
这个部分,每当我们插入一条记录,都会从Free Space
部分(也就是尚未使用的存储空间) 申请一个记录大小的空间,并将这个部分划分到User Records
部分,当Free Space
部分的空间全部被User Records
部分替代掉之后,也就意味着这个页使用完了,如果还有新的记录插入的话,就需要去申请新的页了。
有人会疑问了,图中这个Infimum
+Supremum
是什么?
Infimum
记录 的下一条记录就是本页中主键值最小的用户记录,而本页中主键值最大的用户记录的下一条记录就是 Supremum
记录。但是这两条记录不在User Records
部分,是单独占用的空间,可结合上一张图理解。都是由5
个字节的记录头和8
个字节的一个固定单词组成,如下所示
3.记录头信息的底层原理和计算(show time,难度搞起来)
首先,建个表record_test
CREATE TABLE record_test(c1 INT,c2 INT,c3 VARCHAR(10000),PRIMARY KEY (c1)
) CHARSET=utf8mb4;
执行插入语句
INSERT INTO record_test VALUES(1, 100, 'aa你好'), (2, 200, 'bb哈哈'), (3, 300, 'cc来了'), (4, 400, 'dd在哪');
最终表数据如下
这4
条记录的存储结构示意图如下
注意:
1.图中画记录的时候只选取了记录头的一部分,省略了变长列表和NULL
值列表,但是实际计算的时候要带上。
2.这里把隐藏列省略了,归并到 “其他信息” 里面了
看到这里,你一定和我有着相同的疑问,为什么next_record
显示36
,它表示本条记录真实数据部分到下一条记录真实数据的距离。这个怎么计算的呢?
现在我来和你说说底层那些不为人知的东西。要知道,记录的真实数据除了所有的数据列之外,MySQL
还会为每条记录默认添加一些列(也称为隐藏列),隐藏列也包含在记录的真实数据部分,如下
列名 | 是否必须 | 占用空间 | 描述 |
---|---|---|---|
DB_ROW_ID | 否 | 6字节 | 行ID,唯一标识一条记录 |
DB_TRX_ID | 是 | 6字节 | 事务ID |
DB_ROLL_PTR | 是 | 7字节 | 回滚指针 |
InnoDB
表对主键的生成策略:优先使用用户自定义主键作为主键,如果用户没有定义主键,则选取一个Unique
键作为主键(必须NOT NULL
不允许存NULL
值),如果表中连Unique
键都没有定义的话,则InnoDB
会为表默认添加一个名为DB_ROW_ID
的隐藏列作为主键。
从上表中可以看出:InnoDB
存储引擎会为每条记录都添加 DB_TRX_ID
和 DB_ROLL_PTR
这两个列,但是 DB_ROW_ID
是可选的(在没有自定义主键以及不允许存NULL
值的Unique
键的情况下才会添加该列)。这些隐藏列的值不用我们操心,InnoDB
存储引擎会自己帮我们生成的。
所以刚刚next_record
为36
字节的计算方法就是
6+7(隐藏列2个,因为有自定义主键)=13字节
4(int
长度)+4(int
长度)+8(变长varchar
实际字节数)=16字节
下一列记录的额外信息(变长列表+NULL
值列表+记录头)
1+1+5=7字节
总共13+16+7=36
注意,图中画记录的时候只选取了记录头的一部分,计算的时候直接记录头按
5
字节计算,加上变长列表长度和NULL
值列表长度即可。
如果变长列表NULL
值列表不知道怎么计算长度,见上一篇MySQL的varchar水真的太深了——InnoDB记录存储结构,不看上篇不可能理解的。
而且你可能会疑问为什么第4条记录的下一条却要-123
字节?
前面说过,最大记录的下一条记录是Supremum
记录,而Infimum
记录的heap_no
为0
,而Supremum
记录的heap_no
为1
,存放位置是在所有记录之前,最小记录的heap_no
是从2
开始的。前面给大家看过记录在页中的存储结构,知道Infimum
和Supremum
记录在User Records
之前。
所以最大记录的下一条就是要找到Supremum
记录,那么就要往回走3
条记录和第一条记录的最小记录变长列表+NULL
值列表+头信息(共7
字节),然后加上Supremum
真实数据部分的固定8
个字节。
36*3+7+8=123字节
所以为第4条记录的next_record
为-123
,代表指针往前走123
字节就是下一条记录的真实数据部分的地址。
如果你还细致的观察到Infimum
记录的next_record
是28
,我觉得你挺适合做研究。
在存储结构上,Infimum
记录后面是Supremum
记录,接着才是第一条数据记录。
逻辑上,Infimum
下一条记录是第一条数据记录,所以计算方法是
8(Infimum
固定字节) + 5(Supremum
记录头) + 8(Supremum
固定字节) + 7(第一条数据记录的变长字段列表+NULL
值列表+记录头) = 28字节。
为了更形象的表现next_record
的作用,我们用箭头来替代next_record
的值,注意箭头指向的位置,都是指向真实数据开始的地址。
你可能会疑问,为啥要
next_record
指向记录头信息和真实数据之间的位置呢?指向整条记录的开头位置不好吗?因为这个位置刚刚好,向左读取就是记录头信息,向右读取就是真实数据。我们前边还说过变长字段长度列表、NULL值列表中的信息都是逆序存放,这样可以使记录中位置靠前的字段和它们对应的字段长度信息在内存中的距离更近,可能会提高高速缓存的命中率。
4.当记录被删除,页中记录存储结构如何变化?
当然最大的疑问就是被删除的记录还在页中么?
是的,你以为记录删除了,可它还在真实的磁盘上(占用空间依然存在)。这些被删除的记录之所以不从磁盘上移除,是因为移除它们之后,还需要再磁盘中重新排序其他记录,这会带来一定的性能损耗,所以只是打一个删除标记就可以避免这个问题,首先deleted_mask
设置为1
,然后被删除掉的记录加入到垃圾链表,记录在这个链表中占用的空间称为可重用空间,之后如果有新记录插入到表中的话,它们就可能覆盖掉被删除的这些记录占用的空间。
来演示一下
delete from record_test where c1 = 2;
发现第二条记录被删了
在内存中是怎么样的呢?
删除第2
条记录变化如下
- 第
2
条记录并没有从存储空间中移除,而是把该条记录的delete_mask
值设置为1
。 - 第
2
条记录的next_record
值变为了0
,意味着该记录没有下一条记录了。 - 第
1
条记录的next_record
指向了第3
条记录。 - 最大记录的
n_owned
值从5
变成了4
,因为除了自身Supremum
记录外,还有3
条数据记录(注:Infimum
的n_owned
为1
是因为包含自身算一条记录)
无论怎么对页中的数据进行增删改操作,InnoDB
始终会维护记录的一个单向链表,链表中的各个节点是按照主键值从小到大的顺序链接起来的。
5.当删除的记录再次被插入,页中记录存储结构如何变化?
INSERT INTO record_test VALUES(2, 200, 'bb哈哈');
可以看到,刚刚删除的第二条数据又回来了
内存结构变化如下
InnoDB
并没有因为新记录的插入而为它申请新的存储空间,而是直接复用了原来被删除记录的存储空间。
当数据页中存在多条被删除掉的记录时,这些记录的next_record
属性将会把这些被删除掉的记录组成一个垃圾链表,以备之后重用这部分存储空间。
本篇总结:
本篇主要讲了Infimum+Supremum
部分,分别是页中最小记录的前一个和最大记录的后一个记录,User Records
部分使我们插入的真实数据部分,Free Space
是页总尚未使用的部分。然后讲解了图中next_record
指针地址的计算。
我们知道,页中的记录是单链表,页与页之间是双向链表,其实每个数据页的File Header
部分有上一页和下一页的编号,所以所有数据页会组成一个双向链表。
欢迎一键三连~
有问题请留言,大家一起探讨学习
----------------------Talk is cheap, show me the code-----------------------
MySQL记录删除后竟能按中间被删除的主键加回去,磁盘空间被重用!——底层揭秘MySQL行格式记录头信息相关推荐
- MySQL记录删除后竟能按中间被删除的主键加回去,磁盘空间被重用!
说在前面--本篇也是我读书总结笔记,因为是讲底层原理,我个人认为本文难度是相当高的,可能需要一定的基础. 我将文中的图片包含的计算原理全都演算了一遍,因为书本没有过程详细计算,欢迎大家阅读和仔细推敲. ...
- Mysql 索引(三)—— 不同索引的创建方式(主键索引、普通索引、唯一键索引)
了解了主键索引的底层原理,主键索引其实就是根据主键字段建立相关的数据结构(B+树),此后在使用主键字段作为条件查询时,会直接根据主键查找B+树的叶子结点.除了主键索引外,普通索引和唯一键索引也是如此, ...
- OpenStack虚拟机删除后停在deleting无法正常删除
公司环境中批量删除虚拟机的过程中出现某些虚拟机无法正常删除,任务状态显示删除中但是虚拟无法删除. 通过查询相关文章(下文转载)发现应该是instances row中键值未更新错误导致,但是按照操作云主 ...
- 计算机彻底删除删除后如何恢复,如何完全恢复从计算机删除的文件?
现在,数字产品是我们当代人的最爱. 电脑,手机等是我们的日常用品!但是,电话将在很长一段时间后冻结,并且计算机也将缓慢运行. 然后,我们下载的垃圾清理软件或系统的磁盘清理就可以发挥重要作用. 无疑,这 ...
- springboot 主键重复导致数据重复_程序员:MySQL处理插入过程中主键或唯一键重复值的解决办法
向MySQL插入数据有时会遇到主键重复的场景,原来的做法是先在程序代码中SELECT一下,判断是否存在指定主键或唯一键的数据,如果没有则插入,有的话则执行UPDATE操作,或另外一套逻辑,这种方法是不 ...
- db2有主键时默认hash分区_彻底搞懂 MySQL 分区!
GrimMjxhttps://www.cnblogs.com/GrimMjx/p/10526821.html 一.InnoDB逻辑存储结构 首先要先介绍一下InnoDB逻辑存储结构和区的概念,它的所有 ...
- 主键索引 or 辅助索引?一文告诉你 Mysql limit 优化时的索引选择!
作者 | 吴海存 责编 | 徐威龙 封图| CSDN下载于视觉中国 导读: 本文主要针对limit分页时,是优先基于主键索引还是辅助索引等层面展开分析,对limit及offset的用法以及是否该用索引 ...
- SQL Server中的联合主键、聚集索引、非聚集索引、mysql 联合索引
我们都知道在一个表中当需要2列以上才能确定记录的唯一性的时候,就需要用到联合主键,当建立联合主键以后,在查询数据的时候性能就会有很大的提升,不过并不是对联合主键的任何列单独查询的时候性能都会提升,但我 ...
- mysql中在表中insert数据时,有重复主键id时,变成update
MySQL 自4.1版以后开始支持INSERT - ON DUPLICATE KEY UPDATE语法 例如: id name sex age 1 kathy male 23 2 Javer f ...
最新文章
- CRM WebUI的错误消息是如何从后台服务器取出并绘制到前台的
- 学习java的中文网站_学习java的网站有哪些
- [随笔重写] Python3 的深拷贝与浅拷贝
- “别了,小黄文” 微信打击低俗小说:2019年处理违规账号6.6万+
- 22个ES6面试、复习干货知识点汇总
- 屏蔽关闭百度搜索风云热点的方法
- Windows上SVN服务器搭建【转】
- Java Web开发实战经典 李兴华 PDF pdf
- css中 div圆角边框样式,DIV+CSS圆角边框 - 前端LOVER - 博客园
- 计算机科学与技术的研究背景,计算机科学与技术发展背景
- AISC/FPGA设计中 硬件UART波特率误差计算
- 智能操控装置在高压开关柜中的应用
- python counter怎么用_Counter的基本用法
- 6 款免费网络延迟测试工具-从事网络行业必备
- 博雅数智|3.23直播笔记
- Xming(windows下的X Server)的使用,在windows下运行你的终端和所有基于XWindow的程序
- python数学建模
- vue3 动态传值给子组件
- 玩转96boards之(一)---高通410c板硬件
- “正被停用的激活上下文不是最近激活的”的错误的解决
热门文章
- php导出表格是乱码怎么办,数据库导出excel表格是乱码怎么办-Excel表格乱码问题怎么解决?...
- UML--协作图详解
- 教你快速去掉VC运行环境下的Press any key to continue
- 软件产品登记测试-增值税即征即退
- 中文评价对象提取以及NLP基础
- js判断数组的方法(JavaScript判断数组的方法,7种),实操详细
- Matlab 增加配色方案
- C# jmail收取邮件(带附件)
- 卷积滤波 英文_图形学之卷积滤波器
- FreeCAD学习笔记——Units、Builtin modules和Workbench creation