mysql怎么设置存储记录(MySQL 的一行记录是怎么存储的?)
mysql怎么设置存储记录
MySQL 的一行记录是怎么存储的?
我们先准备好三样东西。
1. 准备好一个 mysql 5.7 并将其启动。
2. 准备好 mysql 的官方文档放在旁边:
https://dev.mysql.com
3. 准备好 mysql 的源码,万一要用呢,别怕:
https://dev.mysql.com/downloads/mysql/5.7.html
一手资料,就是官方文档 + 源码 + 二进制文件,其中二进制文件是我们自己去磁盘中找的,一会就知道了。Let's Go!mysql 会把文件存在哪里呢?先找到他。
- mysql> SHOW VARIABLES LIKE 'datadir';
- +---------------+---------------------------------------------+
- | Variable_name | Value |
- +---------------+---------------------------------------------+
- | datadir | C:\ProgramData\MySQL\MySQL Server 5.7\Data\ |
- +---------------+---------------------------------------------+
- 1 row in set, 1 warning (0.00 sec)
我是 windows,就在这里了,进入这个目录。
这些是啥先不管,盯着它看就好了,我们继续。
第一步:创建数据库
- mysql> create database flash;
盯着刚刚的文件夹看,此时会多出一个文件夹
同时这个文件夹里会多出一个文件,叫:
- |-- flash
- |-- db.opt
- |-- flash
- |-- performance_schema
看一眼它里面的内容,就知道他是干嘛的了。
- default-character-set=latin1
- default-collation=latin1_swedish_ci
default-character-set 是默认字符集,default-collation 是默认字符序。字符集大家都了解,就不展开了。字符序就是字符的排序和比较规则,一般以 _ci 结尾的表示大小写不敏感,_cs 结尾的表示大小写敏感,_bin 结尾的表示用编码值进行比较。含义知道了,那我们重新设置它应该会有所变化,我们把这个数据库设置为开发时常用的 utf8mb4 格式。
- ALTER SCHEMA `flash` DEFAULT CHARACTER SET utf8mb4;
再看 db.opt 文件,内容已经发生了变化。
- default-character-set=utf8mb4
- default-collation=utf8mb4_general_ci
OK,那我们现在对这个文件有了个初步认识,创建一个新的数据库时,首先会多出一个以数据库名为名称的文件夹,然后文件夹里面会多出一个描述数据库配置的 db.opt 文件,我们继续!
第二步:创建表
创建一张 student 表,三列,其中 id 是主键。
- CREATE TABLE `flash`.`student` (
- `id` INT NOT NULL,
- `name` VARCHAR(10) NOT NULL,
- `age` INT NULL,
- PRIMARY KEY (`id`)
- ENGINE = InnoDB
- DEFAULT CHARACTER SET = utf8mb4;
此时 flash 文件夹中,多出了两个文件|-- flash
- |-- flash
- |-- db.opt
- |-- student.frm
- |-- student.ibd
- |-- flash
- |-- performance_schema
为了严谨,我们先看下 db.opt 文件有没有变化,发现没有任何变化,说明创建表对这个 db.opt 配置信息文件,没有影响。
再点开 student.frm,坏了,乱码了。
说明这个文件不是文本文件,用二进制方式打开它。
我把一些关键的地方都标上了含义,那这个文件的作用大家就一目了然了,就是记录表结构嘛,具体的格式可以看 frm 文件结构的官方文档(写得太复杂了...我反正是没看):https://dev.mysql.com/doc/internals/en/frm-file-format.html
db.opt 记录了数据库信息,student.frm 记录了表结构信息,那重头戏自然就在那个 student.ibd 文件了,这里一定存着具体的数据呀,索引呀等信息吧。
打开它!
果不其然还是乱码,那还是二进制打开它!截取了中间信息较为丰富的某部分。
发现一点也看不懂。
第三步:插入数据
我们加几条数据看看。
- INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('1', 'dibingfa2', '2');
- INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('2', 'dibingfa2', '2');
- INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('3', 'dibingfa3', '2');
- INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('4', 'dibingfa4', '2');
- INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('5', 'dibingfa5', '2');
- INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('6', 'dibingfa6', '2');
- INSERT INTO `flash`.`student` (`id`, `name`, `age`) VALUES ('7', 'dibingfa7', '2');
再二进制打开它!
发现有些东西我们可以看出点端倪了!猜测下这部分就是每一行的记录信息吧。
我们插入了七条数据,我发现这些二进制串有一段可以分割成七对,我把他单独拿出来,并且按行分割。
我们将第一行记录拆解,第一行记录的表数据是这样的。
1 dibingfa 2
在 ibd 文件中是这样的。
08 00 00 00 10 00 24 80 00 00 01 00 00 00 00 0A 07 A7 00 00 01 1B 01 10 64 69 62 69 6E 67 66 61 80 00 00 02
这串数据代表啥意思呢?由于本文只能参考官方文档,我们看这里,即 Innodb 行格式。
https://dev.mysql.com/doc/refman/5.7/en/innodb-row-format.html
看这部分(我们的行格式是 DYNAMIC 类型,不过参考 COMPACT 类型描述也行,因为几乎一样,之后再说):
不要看这么一大长串就害怕,我们一点点来,别着急。
这个文档,我可能没资格评价,但我个人觉得写的很烂,一大堆废话也没说明白格式是什么样子,每个字节表示什么。不过也可能作用并非如此。
好的官方文档应该是能把每个字节和每一位都解释清楚的,无奈这个文档不行,那我们就去找更接近一手资料的源码。
我找到了源码,还是很清晰的,注释上就写明了每一行记录的磁盘数据格式,太好了,不用看代码了。
再贴上刚刚的第一行记录。
08 00 00 00 10 00 24 80 00 00 01 00 00 00 00 0A 07 A7 00 00 01 1B 01 10 64 69 62 69 6E 67 66 61 80 00 00 02
来一点点看,第一部分。
| length of the last non-null variable-length field of data ... ...|
...
| length of first variable-length field of data |
这部分是变长字段长度列表,就是依次记录所有变长字段的长度,由于我们只有一个变长字段 varchar(10) 的 name,所以就是 08,我们存储的 "dibingfa" 刚好是 8 个字节,对上了。那如果是多个,很显然,就这样存。
错!应该是这样存,也就是逆序存放,具体为啥后面说。
OK,这就是第一个字节 08 所表示的含义。
再往下。
| SQL-null flags (1 bit per nullable field), padded to full bytes |
第二个结构叫 NUll 值列表,用 1 位表示一个 NULL 值,要填充满一个字节,那往下的一个字节是 00,一看我们的记录中也确实没有 NULL 值,对上了。
具体来说,同样也是逆序存放的。
继续。
| 4 bits used to delete mark a record, and mark a predefined
minimum record in alphabetical order |
| 4 bits giving the number of records owned by this record
(this term is explained in page0page.h) |
| 13 bits giving the order number of this record in the
heap of the index page |
| 3 bits record type: 000=conventional, 001=node pointer (inside B-tree),
010=infimum, 011=supremum, 1xx=reserved |
| two bytes giving a relative pointer to the next record in the page |
ORIGIN of the record
这五个字节很乱,放在一块叫记录头信息,00 00 10 00 24,其表示删除状态,记录类型,下一条记录的相对位置等。
这一大坨先放一放,因为涉及到好多额外的知识。继续往下看。
| first field of data |
...
| last field of data |
剩下全都是具体的列数据了,从第一列到最后一列。第一列是 ID 列,是 INT 类型的 1,占四个字节 80 00 00 01。开头的 80 是因为,正数要以 1 开头,这是 mysql 规定的,0x80 的二进制就是 1000 0000,所以这也对上了。第二列是 name 列,是 "dibingfa" 这样一个 varchar 类型的字符串。可是与后面怎么也对应不上,这是咋回事呢?还记不记得,mysql 每行记录会有几个隐藏列,rowid,事务 ID,回滚指针?没错,就是他们。其中,因为有主键,所以 rowid 就不存在了,也可以说第一列要么是 mysql 为我们生成的 6 字节的 rowid,要么是用户定义的主键或其他 Unique 键,优先以用户定义的键为准。下面我们一块看一下这五个列。(三个隐藏列,两个我们定义的列)
主键 ID:80 00 00 01
事务 ID:00 00 00 00 0A 07
回滚指针:A7 00 00 01 1B 01 10
name 列(dibingfa):64 69 62 69 6E 67 66 61
age 列(2):80 00 00 02
其中 age 列同刚刚说的一样,mysql 会为正数的前面,加一个 1,所以 age 为 2,在磁盘上存储的就是 80 00 00 02。事务 ID 和回滚指针就涉及到事务、隔离级别和 MVCC 这一大坨八股文的知识点,这里不做展开。
行记录格式整体结构
总结下,整个一行记录的格式,叫做 mysql 的行记录格式,ROW_FORMAT。这个 ROW_FORMAT 可以有不同的值,代表存储这一行记录的不同数据结构,其枚举记录在 remOtypes.h 文件中。
- /** Innodb row types are a subset of the MySQL global enum row_type.
- They are made into their own enum so that switch statements can account
- for each of them. */
- enum rec_format_enum {
- REC_FORMAT_REDUNDANT = 0, /*!< REDUNDANT row format */
- REC_FORMAT_COMPACT = 1, /*!< COMPACT row format */
- REC_FORMAT_COMPRESSED = 2, /*!< COMPRESSED row format */
- REC_FORMAT_DYNAMIC = 3 /*!< DYNAMIC row format */
- };
我电脑上用的是 mysql 5.7,其默认的行记录格式是 DYNAMIC,这个在源码中也可以找到答案,在 ha_innodb.cc 中。
- static ulong innodb_default_row_format = DEFAULT_ROW_FORMAT_DYNAMIC;
当然,可以用如下命令查询你的行格式。
- show table status from flash like 'student';
所以我们今天以上讲述的格式,都是 DYNAMIC 格式的结构,总结起来如下:
记录源信息
- 变长字段列表
- NULL 值列表
- 记录头信息
具体记录的各列信息
- rowid 或 主键(隐藏列)
- 事务 ID(隐藏列)
- 回滚指针(隐藏列)
- 列 1
- 列 2
- ...
- 列 n
刚刚那七条记录,整体分析下,就如下图。
然后多个行,一次紧密地排列,通过记录头中的下一条记录的相对位置指针信息,可以快速找到下一条记录的起始位置。再宏观一点看,整个 ibd 文件,划分了很多个块,每个块 16 KB,我们这几行记录信息,在第四个块的某个区域内。
具体为什么是这个区域呢?因为这里是用户记录部分,前面还有文件头、页面头等信息,共占用 120 个字节,咱们今天讲的行记录部分,就从第 121 个字节开始。慢慢的,我再和大家一起把其他部分搞清楚,那 mysql 这块我们就从最原始的磁盘数据入手,将 data 文件夹下的所有文件都搞清楚了。
最原始的数据都搞清楚了,原理还担心么?
再聊几句
其实,不要被行记录格式这种名词吓到,它只是个协议或规定罢了。就是 mysql 规定了一种将一行记录存储在磁盘中的格式,以便于 mysql 自己的程序可以根据这个结构认识这一行记录。
所以这种协议,首先要满足让 mysql 知道全部想知道的信息,比如 mysql 现在能仅仅通过 ibd 文件里的这些二进制数,知道每个字段的值都是什么吗?不能,因为它不知道表结构是什么样子,也就没法知道两个字段值之间的界限在哪里。
所以不难想到,它一定利用了 frm 文件中存储的表结构信息。
其次,要让 mysql 在知道这些信息的同时,还能更方便地利用这个结构,占用更少的存储空间,以及提升程序的便利性。
拿占用更少存储空间这块来讲,NULL 值完全可以当做普通列,也存储在后面,然后规定一个 NULL 值的二进制标识符即可。但 DYNAMIC 行记录格式规定前面放一个 NULL 值列表的结构,并且仅仅用 1 位来表示一个 NULL 值记录,这样就极大节省了空间。再说便利性这块,上面说了变长字段长度列表和 NULL 值列表,都是逆序存储的,看似很别扭,其实就是为了程序的便利性,这里留给大家自己探索吧。
原文链接:https://mp.weixin.qq.com/s?__biz=Mzk0MjE3NDE0Ng==&mid=2247497068&idx=1&sn=36c1d5f9e9be9d864eeb80c8e54cac24&chksm=c2c58fc1f5b206d7d915511487dd0684861e2b5ea9b911794dbaa74c92f4e94837590baa7dcf&mpshare=1&
- mysql出现锁表的原因(导致MySQL做全表扫描的几种情况)
- mysql流式查询(MySQL全面瓦解之查询的正则匹配详解)
- mysql 查询出来的字段拼接(mysql 多个字段拼接的实例详解)
- mysqltext与varchar区别(MySQL CHAR和VARCHAR该如何选择)
- mysql剩余表空间大小(MySQL 表空间碎片的概念及相关问题解决)
- mysql的存储方法(MySQL中的binary类型使用操作)
- mysql如何删除外键约束数据(MySQL中外键的创建、约束以及删除)
- mysql运算符使用方法(MySQL <>和<=> 运算符介绍)
- mysql中json的支持(MySQL中json字段的操作方法)
- mysql和explain哪个好(MySQL EXPLAIN输出列的详细解释)
- windowsmysql服务在哪里(解决windows service 2012阿里云服务器在搭建mysql时缺少msvcr100.dll文件的问题)
- mysql锁技术(MySQL 加锁控制并发的方法)
- redhat6.5安装mysql(Redhat7.3安装MySQL8.0.22的详细教程二进制安装)
- mysql的binlog日志详解(MySQL 有关MHA搭建与切换的几个错误log汇总)
- mysql集群是用什么搭建的(MySQL5.7 集群配置的步骤)
- mysql一主两从架构(MySQL主从搭建多主一从的实现思路与步骤)
- 白龙马的改编学生版,快来看看(白龙马的改编学生版)
- 萌娃唱《白龙马》走红,那生动的小表情,网友直呼 简直是戏精(萌娃唱白龙马走红)
- 朱鹤松被不断认可,凤凰传奇玲花喊话岳云鹏,索要老朱演出门票(朱鹤松被不断认可)
- 元宵晚会槽点多,芒果台上来就假唱,岳云鹏不说相声改评书了(元宵晚会槽点多)
- 岳云鹏跟凤凰传奇谈心,说出了人生中最重要的三个人,这才成功(岳云鹏跟凤凰传奇谈心)
- 爱情可以当饭吃吗(爱情能当饭吃吗)
热门推荐
- mysql行级锁使用教程(浅析MySQL的lru链表)
- sql server中PageLatch和PageIOLatch等待
- python比go语言简单(Python和Go语言的区别总结)
- php连接数据库拒绝使用域名(访问php时提示内存位置访问无效的解决办法和思路分析)
- mysql8.0.25.0安装配置(MySQL8.0.23免安装版配置详细教程)
- vue项目打包上线的方法(vue项目打包以及优化的实现步骤)
- python在mysql创建数据库(python3对接mysql数据库实例详解)
- Python实现合并两个有序链表的方法示例(Python实现合并两个有序链表的方法示例)
- web服务器有几种(2019常见六种web服务器软件介绍)
- amazeui图标(AmazeUI 折叠面板的实现代码)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9