mysql变长类型字段varchar值更新变长或变短底层⽂件存储原理
  为了搞清楚MySQL对于可变长度字段值修改时,如何⾼效操作数据⽂件的机制。之前⼀直模糊不清,⽹上也搜不到现成的答案。经过多⽅资料搜集整理。写出此⽂供⼤家⼀起参阅。由于涉及众多⾮常底层的知识,我假设读者已经对操作系统和磁盘存取有⼀定的基础知识。⽂中如有疏漏,还请⼤佬指正。
mysql存储文档  为了探究这个问题,我们要先来回顾⼀下我之前的⼀篇⽂章《⽂件随机或顺序读写原理深⼊浅出》讲的⽂件存储的底层原理知识。如下图所⽰。⼀个⽂件的数据是以块为单位存储到物理磁盘的随机位置,这是由操作系统负责管理的,⽤户程序⽆权决定。所以在⽂件视图层⾯我们连续存储的数据,映射到物理磁盘层⾯就是随机位置了。图中是假设磁盘块⼤⼩为32KB,则⽂件对应的数据偏移地址存储到对应的物理块中⽰意图。
  我们现在假设MySQL的⼀张表对应⼀个数据⽂件。那么表⾥的数据按⾏存储,则⾏与⾏之间的数据被紧密的连续填充到上图“数据⽂件视图”中,并被以块为单位随机存储到了物理磁盘上。这样遍历表时,就可以从⽂件0地址开始依次读取到所有数据,⾏与⾏之间的间隔符就是普通的换⾏符。每⼀⾏中的字段都按照表结构定义中的字段长度读取即可。这⾥先假设表中的字段都是定长类型的。这样就不会有什么问题。即便是更新某个字段的值,则可以直接使⽤随机⽂件读取⽅法定位到字段的偏移地址写⼊新值即可覆盖旧值。
  那么现在问题来了,如果表中某字段是可变长类型的如varchar(50)。数据插⼊时假设值为“⽉光冷锋”,后⾯有个更新操作,需要将值改为“⽉光冷锋的博客”。此时会发⽣什么呢?由于是可变长度的字段类型,⽂件中该⾏的该字段实际占⽤空间就是四个字符,⽽不是50个字符。且⾏之间数据紧密排列存储。现在值要变成7个字符,则我们⽆法通过简单的随机⽂件存取定位到该字段覆盖旧值,这样会出现严重的问题,会覆盖其他字段或⾏的值。⼀种古⽼的⽂件修改⽅法是,先将要修改的位置后⾯的数据都转移到⼀个临时⽂件中,然后修改现在的位置处的值,最后再把临时⽂件的内容拼接回原⽂件,从⽽实现修改操作。显然这种⽅式⽆法应⽤到频繁更新的数据库上。这种⽅式代价巨⼤,不过⼤多数⽂件都是只读的,极少更新。所以应⽤也挺⼴泛的。⽐如⾳频、视频⽂件。
  那么MySQL是如何解决这类问题的呢?⾸先我们如果把表数据⽂件看成⼀个可以任意发挥的平台,我们不在像其他类型⽂件那样直接将数据⼀个挨着⼀个紧密的写⼊⽂件的偏移地址中。MySQL使⽤页
这个概念重新对⽂件视图划分,也就是⼀个页对应⼀段⽂件的偏移地址。如下图所⽰。MySQL页⼤⼩默认16kB,刚好对应图中两个页对应到⼀个物理块32kb⼤⼩。MySQL的页使⽤双向链表⽅式组织。
  下⾯我们来看下如果可变长度varchar类型的字段值变长了,⽂件⾥的数据该怎么存储呢?因为表中数据刚开始插⼊时,可变长度字段值都是根据实际长度存储下来的,且⾏与⾏之间数据也是紧密连续存放在⽂件地址中的(再次强调⼀下,不是连续存放在物理磁盘上的)。那么现在值变长了,原来的位置⽆法扩展出新的空间出来,所以⽆法覆盖存放到原来的位置上。此时MySQL就会使⽤页分裂的⽅法扩展字段变长的空间。
  ⽐如假设⾏10的数据存放在页③的位置,如下图2所⽰。⾏11也是存放在页③的位置,且他们两⾏都把页③填满了。现在更新⾏10的某个变长字段值,由“⽉光冷锋”改成“⽉光冷锋的博客”。MySQL将新创建⼀个页⑥出来(相应的原数据⽂件也要变⼤增长),把原来页③的内容,⾏10和⾏11的数据重新⽣成排列⼀遍存储到页③和页⑥中,同时将原来的页链表结构重新修改其前驱和后继页节点的指针就OK了。如下图3所⽰。
图2        图3
  MySQL就是通过这种技巧,实现了修改数据⽂件时,不必像传统修改⽂件那样付出昂贵代价。这种⽅式虽然解决了修改⽂件时避免⼤规模移动数据的弊端,但是读取这些数据时,却⽆法像传统存取⽅
式那样,直接从⽂件偏移地址0开始顺序读取。⽽是要根据页的链表结构顺序读取。需要不断的计算和移动⽂件偏移量指针,好在这个过程不会花费多少代价。但是会带来另外⼀个⽐较严重的问题就是页空洞,也称为碎⽚。上⾯⾏11有部分字段数据已经转移到了页⑥中,显然页⑥是没有存满的。⾏12是存在页④中的,这样就产⽣了碎⽚问题,浪费了⽂件的⼀些地址空间,这些空洞存的都是特殊占位符,也要占据真实的物理磁盘空间。随着更新删除操作越来越多,碎⽚也会越来越多,所以有必要定期进⾏表的碎⽚整理,这样可以收缩表⽂件占据的磁盘空间。也可以降低页链表的长度,从⽽节省⼀些寻址操作代价。
  如果可变长字段值由⼤变⼩,则原来的字段值地址空间⾜够了,也就不需要新加页了,只需要重新整理排列⼀下当前更新的⾏数据即可。使得变成字段的值占⽤实际空间即可。⾄于留下的页碎⽚问题,MySQL也有相应的机制做合并优化操作。我这⾥不做深究。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。