为了搞清楚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也有相应的机制做合并优化操作。我这里不做深究。
 
 

mysql变成类型字段varchar值更新变长或变短底层文件存储原理的更多相关文章

  1. MySQL数据类型 int(M) 表示什么意思?详解mysql int类型的长度值问题

    MySQL 数据类型中的 integer types 有点奇怪.你可能会见到诸如:int(3).int(4).int(8) 之类的 int 数据类型.刚接触 MySQL 的时候,我还以为 int(3) ...

  2. EtherType :以太网类型字段及值

    Ethernet II即DIX 2.0:Xerox与DEC.Intel在1982年制定的以太网标准帧格式.Cisco名称为:ARPA Ethernet II类型以太网帧的最小长度为64字节(6+6+2 ...

  3. mysql int类型字段插入空字符串时自动转为0

    mysql int类型字段插入空字符串时自动转为0 如果不想转的话可以修改配置文件 修改 my.ini 文件. # Set the SQL mode to strictsql-mode=”STRICT ...

  4. 关于Java读取mysql中date类型字段默认值'0000-00-00'的问题

    今天在做项目过程中,查询一个表中数据时总碰到这个问题:      java.sql.SQLException:Value '0000-00-00' can not be represented as ...

  5. mysql列类型char,varchar,text,tinytext,mediumtext,longtext的比较与选择

    储存不区分大小写的字符数据 TINYTEXT 最大长度是 255 (2^8 – 1) 个字符. TEXT 最大长度是 65535 (2^16 – 1) 个字符. MEDIUMTEXT 最大长度是 16 ...

  6. Mysql各种类型字段长度

    1.数值类型 列类型 需要的存储量 TINYINT 1 字节 SMALLINT 2 个字节 MEDIUMINT 3 个字节 INT 4 个字节 INTEGER 4 个字节 BIGINT 8 个字节 F ...

  7. mysql 字符串类型 char varchar

    字符类型用在存储名字.邮箱地址.家庭住址等描述性数据   char指的是定长字符,varchar指的是变长字符 #官网:https://dev.mysql.com/doc/refman/5.7/en/ ...

  8. mysql int类型的长度值

    整数类型的存储和范围(来自mysql手册) 类型 字节 最小值 最大值     (带符号的/无符号的) (带符号的/无符号的) TINYINT 1 -128 127     0 255 SMALLIN ...

  9. 详解mysql int类型的长度值问题【转】

    mysql在建表的时候int类型后的长度代表什么? 是该列允许存储值的最大宽度吗? 为什么我设置成int(1), 也一样能存10,100,1000呢. 当时我虽然知道int(1),这个长度1并不代表允 ...

随机推荐

  1. QQ三国 秘制机簧去哪打?打的太慢?

    我在完成这个任务时卡了很久,因为打的效率极低,因此最后我是如何完成的. 1. 先说打谁吧,刚开始我打机簧蜘蛛,就没打出来过,,后来换了机簧车,掉率就上升了,建议打机簧车. 2. 如果你一直打不出来,建 ...

  2. bzoj1341 名次排序问题rank sorting(dp,考虑到对未来的贡献)

    QWQ啊 这个题可以说是我目前碰到过的最难理解的dp之一了. 题目大意: 已知参赛选手的得分,你的任务是按照得分从高到底给出选手的排名.遗憾的是,保存选手信息的数据结构只支持 一种操作,即将一个选手从 ...

  3. HTTP标签

    系统的http状态码知识,我是在<图解http里学习的>. 状态码的职责是告知从服务器端返回的请求结果. 分类如下: 2XX --> 成功 200 OK(一般情况) 204 No C ...

  4. CentOS 文本编辑器

    目录 1.Nano 1.1.基础命令 1.2.快捷操作 1.3.配置文件 2.Vim 2.1.四大模式 2.2.基础命令 2.3.标准操作 2.4.高级操作 2.5.配置文件 Linux 终端的文本编 ...

  5. 【Java虚拟机8】自定义类加载器、类加载器命名空间、类的卸载

    前言 学习类加载器就一定要自己实现一个类加载器,今天就从一个简单的自定义类加载器说起. 自定义类加载器 例1 一个简单的类加载器,从一个给定的二进制名字读取一个字节码文件的内容,然后生成对应的clas ...

  6. .Net Core中使用ElasticSearch(一)

    一.安装配置 在官网下载Es,注意版本号,不同大版本号之间差异很大.我安装的是7.14.0版本 1.1 安装成服务 cmd 进入bin目录下执行 elasticsearch-service.bat i ...

  7. python标准库glob 递归目录下所有文件

    import glob for i in glob.glob(r'C:\Desktop\**',recursive=True): print(i) """ re:?*[0 ...

  8. Spark面试题整理(三)

    1.为什么要进行序列化序列化? 可以减少数据的体积,减少存储空间,高效存储和传输数据,不好的是使用的时候要反序列化,非常消耗CPU. 2.Yarn中的container是由谁负责销毁的,在Hadoop ...

  9. P2472 [SCOI2007]蜥蜴(最大流)

    P2472 [SCOI2007]蜥蜴 自己第一道独立做题且一遍AC的网络流题纪念... 看到这道题我就想到网络流建图的方式了... 首先根据每个高度,我们将每个点拆成两个点限流.之后根据跳的最大距离, ...

  10. mysql 导入sql文件

    navicat 工具导入 1.连接数据库后,右键选择导入sql文件 2.选择sql文件,开始导入 4.过程图 5.结果图