【MySQL】表中的一条数据在磁盘上是如何存放的?

这篇具有很好参考价值的文章主要介绍了【MySQL】表中的一条数据在磁盘上是如何存放的?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 InnoDB行格式

不同的存储引擎一般是为实现不同的特性来开发的,真实数据在不同存储引擎中的存放格式一般是不同的,甚至有的存储引擎(比如 MEMORY)都不用磁盘来存储数据,也就是对于使用 MEMORY 存储引擎的表来说,关闭服务器后表中的数据就消失了。 lnnoDB是MySQL 默认的存储引擎,也是我们最常用到的存储引擎。

当我们想从表中获取某些记录时, lnnoDB 存储引擎需要一条一条地把记录从磁盘上读出来么?不,这样重复访问磁盘会非常非常非常慢😶,lnnoDB 采取的方式是,将数据划分为若干个页 ,以页作为磁盘和内存之间交互的基本单位。lnnoDB 页的大小一般为16KB = 16384B 。也就是在一般情况下,一次最少从磁盘中读取 16KB 的内容到内存中,一次最少把内存中的 16KB 内容刷新到磁盘中。

Tips:系统变量innodb_page _size只能在第一次初始化MySQL数据目录时指定,运行过程中不可更改页面大小。

我们平时都是以记录为单位向表中插入数据的,这些记录在磁盘上的存放形式也被称为行格式或者记录格式,可以在创建或修改表的语句中指定记录所使用的行格式。目前有4种行格式:

  • COMPACT:占用空间较小,适合具有大量重复数据的表。
  • REDUNDANT:不再被推荐使用,用于兼容旧版本。
  • DYNAMIC:支持更多的数据类型和功能,适用于各种情况。
  • COMPRESSED:将数据进行压缩,可以显著减小存储空间,但可能会增加CPU负担。

1.1 COMPACT行格式

1.1.1 记录的额外信息

【MySQL】表中的一条数据在磁盘上是如何存放的?,mysql

1、变长字段长度列表(如果无变长字段,没有该部分)

变长字段列表仅存储非NULL列。

VARCHAR(M)、 BINARY(M)、各种 TEXT 类型、各种 BLOB 类型等变长字段占用的存储空间分为两部分:真正的数据内容 + 该数据占用的字节数。COMPACT 行格式中,所有变长字段的真实数据占用字节数都存放在记录的开头位置,各变长字段的真实数据占用的字节数按照列顺序逆序存放

1个字节能表示的最大值为0xFF = 255,2个字节能表示的最大值为0xFFFF = 65535。给定以下符号表示。

  • W W W:某个字符集中最多需要W字节来表示一个字符,比如utf8mb4字符集中的W就是4

  • M M M:对于变长类型 VARCHAR(M),能存储最多M个字符

  • L L L:该变长字段实际存储的字符串占用的字节数是 L

如果该变长字段允许存储的最大字节数 M × W M\times W M×W 超过 255 字节,并且真实数据占用的字节数 L L L超过 127 字节,则使用2字节来表示真实数据占用的字节数,否则使用1字节。

如果某个字段占用的字节数特别多, InnoDB 有可能把该字段的值的一部分数据存放到所谓的溢出页中。

对于 CHAR(M)类型的列来说,当列采用的是定长编码的字符集时,该列占的字节数不会被加到变长字段长度列表:而如果采用变长编码的字符集时 该列占用的字节数就会被加到变长字段长度列表。

2、NULL值列表(如果无NULL值,没有该部分)

COMPACT 行格式把一条记录中值为 NULL 的列统一管理起来 ,存储到 NULL 值列表。对表中未限制Not Null的列,按列顺序逆序存储二进制位,1代表NULL,0代表不为NULL。MYSQL规定NULL值列表必须为整数字节,若不足,在高位补0。

3、记录头信息(固定5字节)

deleted_flag:这些被删除的记录之所以不从磁盘上移除,是因为在移除它们之后 还需要在磁盘上亟新排列其他的记录 这会带来性能消耗,所以只打一个删除标记就可以避免这个问题.所有被删除掉的记录会组成一个垃圾链衰,记录在这个链表中占用的空间称为可重用空间。

【MySQL】表中的一条数据在磁盘上是如何存放的?,mysql

1.1.2 记录的真实数据

  • row_id:如果我们建表的时候指定了主键或者唯一约束列,那么就没有 row_id 隐藏字段了。如果既没有指定主键,又没有唯一约束,那么 InnoDB 就会为记录添加 row_id 隐藏字段。row_id不是必需的,占用 6 个字节。
  • trx_id:由哪一个事务生成
  • roll_pointer:这条记录上一个版本的指针

【MySQL】表中的一条数据在磁盘上是如何存放的?,mysql

1.2 Dynamic & ComPRESSED

如果某个字段占用的字节数特别多, InnoDB 有可能把该字段的值的一部分数据存放到所谓的溢出页中,在记录的真实数据处只会存储该列的部分数据,而把剩余的数据分散存储在几个其他的页中 然后在记录的真实数据处用 20 字节存储指向这些页的地址。

Compressed 和 Dynamic 这两个行格式和 Compact 非常类似,主要的区别在于处理行溢出数据时有些区别。

这两种格式采用完全的行溢出方式,记录的真实数据处不会存储该列的一部分数据,只存储 20 个字节的指针来指向溢出页。而实际的数据都存储在溢出页中。

【MySQL】表中的一条数据在磁盘上是如何存放的?,mysql

1.3 VarChar(n)中n的最大取值?

MySQL规定:一行记录除了 TEXT、BLOBs 类型的列(不包括隐藏列和记录头信息),限制最大为 65535 字节。

SO:要保证所有字段的长度 + 变长字段字节数列表所占用的字节数 + NULL值列表所占用的字节数 <= 65535

2 InnoDB页格式

2.1 数据页组成

记录是按照行来存储的,但是数据库的读取并不以「行」为单位,否则一次读取(也就是一次 I/O 操作)只能处理一行数据,效率会非常低。

因此,InnoDB 的数据是按「数据页」为单位来读写的,也就是说,当需要读一条记录的时候,并不是将这个记录本身从磁盘读出来,而是以页为单位,将其整体读入内存。

数据库的 I/O 操作的最小单位是页,InnoDB 数据页的默认大小是 16KB,意味着数据库每次读写都是以 16KB 为单位的,一次最少从磁盘中读取 16K 的内容到内存中,一次最少把内存中的 16K 内容刷新到磁盘中。

【MySQL】表中的一条数据在磁盘上是如何存放的?,mysql

2.2 最大、最小记录与用户记录

我们自己存储的记录会按照指定的行格式存储到User Records 部分。但是在一开始生成页的时候,其实并没有 User Records 部分,每当插入一条记录时都会从 Free Space部分(也就是尚未使用的存储空间)申请一个记录大小的空间,并将这个空间划分到User Records 部分。当 Free Space 部分的空间全部被 User Records 部分替代掉之后,也就意味着这个页使用完了,此时如果还有新的记录插入,就需要去申请新的页了 。

设计InnoDB 的大叔把记录一条一条亲密无间排列的结构称之为堆 (beap) 。为了方便管理这个堆,他们把一条记录在堆中的相对位置称之为heap_no ,放在一条记录的记录头信息中。

【MySQL】表中的一条数据在磁盘上是如何存放的?,mysql

对于一条完整的记录来说,比较记录的大小就是比较主键的大小。设计InnoDB 的大叔设计了两个虚拟记录,规定infimum记录为一个页面中最小的记录,supermum记录为一个页面中最大的记录。

2.3 页目录

数据页中的记录按照「主键」顺序组成单向链表,单向链表的特点就是插入、删除非常方便,但是检索效率不高,最差的情况下需要遍历链表上的所有节点才能完成检索。因此,数据页中有一个页目录,起到记录的索引作用。

页目录创建的过程如下:

  1. 将所有的记录划分成几个组,这些记录包括最小记录和最大记录,但不包括标记为“已删除”的记录;
  2. 每个记录组的最后一条记录就是组内最大的那条记录,并且最后一条记录的头信息中会存储该组一共有多少条记录,作为 n_owned 字段(图中粉红色字段)
  3. 页目录用来存储每组最后一条记录的地址偏移量,这些地址偏移量会按照先后顺序存储起来,每组的地址偏移量也被称之为槽(slot),每个槽相当于指针指向了不同组的最后一个记录

【MySQL】表中的一条数据在磁盘上是如何存放的?,mysql

页目录就是由多个槽组成的,槽相当于分组记录的索引。因为记录是按照「主键值」从小到大排序的,所以我们通过槽查找记录时,可以使用二分法快速定位要查询的记录在哪个槽(哪个记录分组),定位到槽后,再遍历槽内的所有记录,找到对应的记录,无需从最小记录开始遍历整个页中的记录链表。

「槽对应的值都是这个组的主键最大的记录,如何找到组里最小的记录」?找到上一个槽最大元素的下一个记录。文章来源地址https://www.toymoban.com/news/detail-641251.html

到了这里,关于【MySQL】表中的一条数据在磁盘上是如何存放的?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 用mysql实现按条件分组并且每组去时间最大的一条

    可以考虑使用子查询或者连接查询实现。以下是两种语法: 其中,组内条件是按照哪个字段进行分组的条件,可以是一个或多个字段。时间是需求中要筛选的时间字段,假设为time字段。这个查询会返回满足组内条件下时间最大的记录。 其中,组内条件和时间的含义同上。这

    2024年02月16日
    浏览(40)
  • MySQL 删除表中的数据记录详解

    目录 前言言 一、删除表中的数据记录? 1.删除特定数据记录 2.删除所有数据记录 总结         删除数据记录是数据操作中常见的操作,可以删除表中已经存在的数据记录。在MySQL中可以通过DELETE语句来删除数据记录,该SQL语句可以通过以下几种方式使用:删除特定数据记

    2024年02月13日
    浏览(34)
  • 一条SQL如何被MySQL架构中的各个组件操作执行的?

    简单用一张图说明下, MySQL 架构有哪些组件,接下来给大家用 SQL 语句分析 假如 SQL 语句是这样 SELECT class_no FROM student WHERE name = \\\'lcy\\\' AND age 18 GROUP BY class_no 其中 name 为索引,我们按照 时间顺序 来分析一下 客户端:客户端(如 MySQL 命令行工具、 Navicat 、 MySQL Workbench 或其他应

    2023年04月22日
    浏览(38)
  • 怎么有效地查询MySQL表中的重复数据

    在MySQL数据库中,偶尔会遇到需要查找表中出现的重复数据的情况。这种情况下,我们可以通过编写一些SQL查询语句轻松地找到并处理这些重复行。本文将介绍一些常见的方法和技巧,帮助你有效地查询MySQL表中的重复数据。 方法一:使用COUNT()函数查询重复行 COUNT()函数是M

    2024年02月07日
    浏览(31)
  • MYSQL 查询数据库中所有表中的数据量

    SELECT TABLE_NAME, TABLE_ROWS  FROM INFORMATION_SCHEMA.TABLES  WHERE TABLE_SCHEMA = \\\'your_database_name\\\'; 将 your_database_name 替换为您实际使用的数据库名称。 执行以上查询语句后,将会获取到 your_database_name 数据库中所有表的数据量,其中 TABLE_NAME 列表示表名称, TABLE_ROWS 列表示表中的数据量。

    2024年02月11日
    浏览(53)
  • ORACLE多列中取出数据最大的一条

    当查询出来的数据存在多条数据时,想按照一定条件排序取出其中一条数据。 row_number() over( partition by 分组字段 order by 排序字段 desc) --根据table_a中的pk_house,pk_customer进行分组,然后根据table_b.billdate进行排序,取出最大billdate的一条数据 select *   from (select  table_a.code,    

    2024年02月07日
    浏览(29)
  • Oracle或者MySQL 将一个表中的数据插入到另外一个表中(表名不同,表字段也可能不同)

    在Oracle或者MySQL中,如何将一个表中的数据插入到另外一个表中(表名不同,字段名可能也不同),以Oracle数据库为例。 (1)创建一个包含所需字段的新表 (2)使用INSERT INTO SELECT将数据从旧表复制到新表。在SELECT语句中,选择需要从旧表中复制的字段。

    2024年02月15日
    浏览(34)
  • Django如何删除数据库表中的数据【不断积累】

    这篇博客积累Django的数据库常用删除方法。 假设有表模型Author定义如下: Django 默认为每个模型添加一个名为 id 的自增主键列,用于唯一标识每条记录。因此,可以使用该 id 值来删除指定的记录。 下面是使用 id 值删除记录的示例代码: 在这个示例中,我们首先指定要删除

    2024年02月13日
    浏览(41)
  • Sql group by 分组取时间最新的一条数据

    1.取时间最新的记录 不分组有重复(多条CreateTime一样的都是最新记录) 2.分组后取时间最新的记录 3.如果Id是uuid类型无法使用max(id)的解决办法(使用开窗函数)

    2024年02月11日
    浏览(44)
  • 数据库将一张表中的数据更新到另一张表(Oracle、MySQL)

            方式一(推荐)         方式二         方式一比方式二效率快很多

    2024年02月07日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包