mysql详情之关于数据页的一些思考

相关概念

说到mysql的存储格式,很多人都知道是数据页。网上很多资料也都会放上类似这样几个图,讲解数据页各个部分的用途

在这里插入图片描述

数据页之间通过双向链表连接

在这里插入图片描述

数据行,就是上图的“记录x”。具体格式如下

在这里插入图片描述

问题

网上资料很多,但还是会产生很多费解的问题,下面这一堆问题也许就有你的疑问:

数据页的特点是什么?是只既规定格式,还是主要在固定大小?

我怎么听说还和磁盘数据块大小有关?

mysql索引结构是b+树。按我们朴素的设想,mysql应该分别给索引和行数据存储各不同的格式,真实情况是这样吗?

辅助索引看起来一整棵树都是索引节点,是不是和主键索引存储格式不一样?

好像大家还说一个索引块就是一个数据页,是这样的吗?他俩的大小为什么要相等,一个索引块用两个数据页表示不行吗?

mysql为什么要固定数据页大小?索引有多大就取多大呗,数据也是一样,只固定个格式不就行了。有多少数据,按照格式一直往后排呗?

mysql数据表文件(.ibd)里是怎么构建出一颗树的,内存里构建树用指针地址连接节点,磁盘里的文件怎么表示?

加载查询时,是把磁盘里的树结构,直接复制到内存,产生一颗树结构吗?

mysql数据表文件(.ibd)是前半部分是索引,后半部分是数据吗?还是混在一起的?

删除数据,会不会让数据表文件牵一发动全身的调整(地址挪动问题)?

上面关于数据页的格式里,有一项是“所属层级”。可当我们按照B+树新增数据时,是一会儿加到叶子节点,一会儿加到非叶子节点。往数据页里写数据时,不会导致数据页里包含多个层级的数据吗?

当把数据页,索引,索引里的索引数据,行数据,索引结构,磁盘块这一堆概念混在一起。还有一个黑盒一般的数据库文件,就会产生很多费解的地方。

概念梳理

数据页

也就是本文的主题。可以类比为:集装箱(这个类比很重要,能解决上面的很多不太容易想明白的问题)。

集装箱大家都见过,大小都一样。路上的集装箱车,远洋万吨货轮上,全世界各大港口里都是有。我们的一切生活物资基本都是由集装箱装运的。之所以看起来都一样,因为集装箱有标准。外观,大小都有标准。

数据页也是一样,既规定格式(前面图示的),也规定大小,图示上表示为16k。这个可配置,我们自己也可以通过命令查看

SHOW VARIABLES LIKE 'innodb_page_size';

通过这个参数的名字,我们就推断出:innodb的页大小,不分数据页/索引页。因为如果分的话,这个参数将无法进行区分。

如图所示这个熟悉的b+树

在这里插入图片描述

准确的讲,这个b+树图里没有数据页,只有数据。现在我给他们套了一层外壳,那层红色外壳 就相当于数据页。就像集装箱,大小都一样,里面的货物不一样。

那你可能会疑惑:索引才一个字段值,数据行那么多字段(最多可达1000个字短),都用一样的集装箱来装?

答:是的。一个数据页能装一千多个索引元素。 如果按一张表10个字段来算,只能装一百多行数据。(mysql要求上限是1000个字段大概也是这个意思:一行数据正好装在一个数据页里)

还有前面的一个问题:辅助索引的存储方式。这里就不表示了。和主键索引一样:数据大就少存几个,数据小就多装点。

为什么要给数据页固定大小

如果让我们设计数据库,估计没多少人会想到先引入固定大小数据页这一概念。要知道事务这么重要的机制都不是mysql一开始就考虑的。

朴素的想法就是:有多大存多大呗,可以定格式(就比如按上面图示的格式,只是中间存记录的大小不固定),把大小都定下来就显得很死板了。

这个问题我搜了很多资料,暂时没找到很有说服力的理由。但类似分块的思想却是工业上很常见的“最佳实践”。下面是我自己的一些思考内容(有其他想法可以评论留言)。

这来源于对生活的观察(联想到集装箱到作用),还有文件系统(如Hadoop),磁盘里的数据块。尤其是我们熟悉的集装箱,我感觉应该是类似的:看起来集装箱即便没有标准,也不影响运输货物,无非就是箱子有大有小。

下面是我对分块的一些思考

在这里插入图片描述

分块和分层一样,都有三个好处:

  1. 屏蔽复杂系统,简化设计思路
  2. 统一标准,降低交流成本
  3. 空间局部性原理(物以类聚)

分块还有一个好处:把连续现实世界离散化处理,优化利用存储空间资源

相比较分层的显而易见(由于功能,空间,时间等区别),分块看起来就有点“经验主义”,把原本应该属于一体(或没有明显边界)的东西,硬生生的割裂成等长的块。比如磁盘扇区,文件系统的数据块,集装箱等。

数据页的大小是怎么定的

前面我们知道,数据页要定一个大小,配置好后面就改不了。mysq5.7是默认是16k。那这个大小是怎么定的呢。

它是根据索引块(b+树的节点)大小定的,也就是让数据页=索引块。而索引块之所以定16k,那是根据当前mysql的正常存储数据量定的,保证三层索引结构存储存储百万数据。最后取一个接近数据页,并且是4k的倍数的,以方便数据存储(当然,这个默认大小可能随着mysql的发展,也是会变的)。

那么为什么要让数据页=索引块?

如果索引块比数据页小:那就意味着每次读区一个索引块,就有一部分存储空间被浪费(用一个大桶打水,却只装一杯)。

如果索引块比数据页大:那就意味着每次需要读多次连续的数据页,才能完整取出一个索引块。但这样会导致多次io,为什么?

因为一旦分块,就不要有取多块连续数据的想法了。数据页就像一个个不透明的箱子,挨着的两个箱子内部数据未必有什么关联(可能被优化存储过,或者刚好废物利用,存到之前删除数据的位置)。想取多块,就要分多次取,而msyql每次磁盘io都极浪费时间。

所以让数据页=索引块 是性价比最高的选择

innoDB中一切皆索引

这句话不是我说的,而是一个mysql开发大佬Jeremy Cole说的。意在强调:不要看到b+树示意图,自然觉得非叶子结点和叶子几点的数据是分不同方式存储的。

mysql底层设计的比你想象的更加统一。有了数据页这个统一集装箱后,所有数据都一视同仁的被装进了这样的标准大小的箱子里。而前面我们说了,索引块=数据页。如图所示b+树更细致示意图

在这里插入图片描述

从图中可以看出:

  1. 数据页大小都一样,数据行和索引都存在这样的容器里
  2. 节点4是非叶子节点,其他是叶子节点
  3. 非叶子节点中存放的是索引数据,索引很小,所以可以存很多
  4. 叶子节点中存有数据行,比较大。所以相比较而言,一个数据页存个数的少一些
  5. 非叶子节点存的是叶子节点的“页码”,通过“页码”找下层结点

如果按照b+树的增加数据的顺序,用b+树模拟器,模拟一下增加数据的流程。

数据一会儿加第一层的,一会儿加第二层的。但并不意味着同一个数据页就可能同时存在两层的数据。

比如上图中page3写完,还需要继续加数据行。此时并不会因为page4有空,就往page4里写,而是会新增一个page5。我们把增加的数据按顺序排一下,就像下图这样

在这里插入图片描述

可以大致理解为,这就是数据库表文件的样子。从图中可以看出:

  1. 数据页,页码是按顺序增加的。
  2. 顺序的页码+固定的页大小。使得:只要知道页码就能定位到具体的页。这就是在文件里,mysql是如何构建b+的。
  3. 数据文件(如.ibd文件)中的内容可能在多个层之间反复横跳,并不是一半索引一半数据。

由于索引块和数据页一样大,物理位置上也基本重合,所以这两个概念可以统称为一个东西:index(不是平时应用数据库时,用的索引的那个概念),也可以像其他一些文章所说的:索引即数据,数据即索引

删除数据

当我们删除数据后,mysql数据库文件大小不会马上有变化。而是做个删除标记,等待数据被后面新增数据重新覆盖再利用。毕竟看上面的数据文件格式,如果立马覆盖删除数据,会牵一发动全身。

其他

当通过b+树检索到具体的数据页之后,就要在数据页内找数据。由于数据页也不小,存储索引可达一千多个。所以也需要数据结构和算法。在数据页内,数据通过偏移量进行单向连接,以实现顺序读取。 并且对数据进行分组,每组5-8个数据。

通过二分法找到组,然后在组内顺序读取找数据。如图所示

在这里插入图片描述

这让我想起刚学二分法时就产生过的一个疑惑:二分法面对大量有序数据,一开始效率确实很高,一次把范围缩小一半,但越到后面就给人一种“效率越来越低”的感觉。mysql这里设计就是这个意思:

二分法是有适用范围的,大概是10到100000。太小的话,内存里还是顺序读的快。太大的话(百万以上并且节点数据占空间),全部数据放在内存里不合适,用b+树更合适。所以mysql数据页内用分组的方式,避免了在数据量很少的时候还“左右横跳的找数据”。

mysql更详细的存储格式请参考mysql底层设计图(Jeremy Cole的)

总结

  1. b+树中的节点既可以称为索引块,也可以称为数据页,是一样大的。数据页即规定格式,也规定大小。或者说:数据即索引,索引即数据
  2. 数据页在文件中顺序排放,通过页码进行双向关联,构建出b+树(节点中的数据通过偏移量进行单向关联)
  3. 数据页的功能:优化存储空间(写),方便检索定位数据(读)。这种类似的分块思想在数据存储/传输中都有应用

参考

一文搞懂MySQL索引页结构

关于InnoDB表数据和索引数据的存储
mysql底层设计图(Jeremy Cole的)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值