先准备三样东西。1、准备一个mysql5.7,启动。2.准备好mysql的官方文档,放在旁边:https://dev.mysql.com3.准备好mysql的源码,万一需要用到,别怕:https://dev.mysql.com/downloads/mysql/5.7.html第一手资料是官方文档+源代码+二进制文件。二进制文件是我们自己在磁盘上找到的,一会儿就知道了。Let'sGo去哪儿!mysql存储文件?先找到他。mysql>SHOWVARIABLELIKE'datadir';+----------------+------------------------------------------+|Variable_name|Value|+------------+--------------------------------------------+|datadir|C:\ProgramData\MySQL\MySQLServer5.7\数据\|+----------------+----------------------------------------------+1rowinset,1warning(0.00sec)我是windows,我来了,进入这个目录。不管这些是什么,盯着看就行了,我们继续。第一步:创建数据库mysql>createdatabaseflash;盯着刚才那个文件夹,会多出一个文件夹,这个文件夹里多出一个文件,叫做:|--flash|--db.opt|--flash|--performance_schema看看它的内容就知道它是干什么的了。default-character-set=latin1default-collat??ion=latin1_swedish_cidefault-character-set是默认字符集,default-collat??ion是默认字符顺序。字符集大家都知道,就不展开了。字符顺序是字符的排序和比较规则。一般以_ci结尾的表示不区分大小写,以_cs结尾的表示区分大小写,以_bin结尾的表示使用编码值进行比较。意思是已知的,然后我们重置它,它应该改变。我们将这个数据库设置为开发时常用的utf8mb4格式。ALTERSCHEMA`flash`DEFAULTCHARACTERSETutf8mb4;再看db.opt文件,内容变了。default-character-set=utf8mb4default-collat??ion=utf8mb4_general_ciOK,现在我们对这个文件有了一个初步的了解,在新建数据库的时候,首先会多出一个以数据库命名的文件夹,然后文件夹里面会有一个更多描述数据库配置的db.opt文件,让我们继续!第2步:创建表创建一个包含三列的student表,其中id是主键。CREATETABLE`flash`.`student`(`id`INTNOTNULL,`name`VARCHAR(10)NOTNULL,`age`INTNULL,PRIMARYKEY(`id`)ENGINE=InnoDBDEFAULTCHARACTERSET=utf8mb4;此时在flash文件夹中,多了两个文件|--flash|--flash|--db.opt|--student.frm|--student.ibd|--flash|--performance_schema为了严谨起见,看看有没有先改一个db.opt文件,发现没有变化,说明建表对db.opt配置信息文件没有影响。然后点student.frm,坏了,乱码。说明这个文件不是a文本文件,用二进制方式打开,我把一些关键的地方都标出了含义,所以这个文件的作用大家一目了然,就是记录表的结构,具体格式可以参考frm文件结构的官方文档(太复杂了。。。反正我没有。看):https://dev.mysql.com/doc/internals/en/frm-file-format.htmldb.opt记录数据库信息,student.frmre整理表结构信息,亮点自然是学生。ibd文件,这里必须有具体的数据、索引等信息。打开它!果然还是乱码,所以用二进制打开!截取中间信息比较丰富的某个部分。发现什么都不懂。第三步:插入数据我们来添加几条数据来看看。INSERTINTO`flash`.`student`(`id`,`name`,`age`)VALUES('1','dibingfa2','2');INSERTINTO`flash`.`student`(`id`,`name`,`age`)VALUES('2','dibingfa2','2');INSERTINTO`flash`.`student`(`id`,`name`,`age`)VALUES('3','dibingfa3','2');INSERTINTO`flash`.`student`(`id`,`name`,`age`)VALUES('4','dibingfa4','2');INSERTINTO`flash`.`student`(`id`,`name`,`age`)VALUES('5','dibingfa5','2');INSERTINTO`flash`.`student`(`id`,`name`,`age`)VALUES('6','dibingfa6','2');插入`flash`.`student`(`id`,`name`,`age`)VALUES('7','dibingfa7','2');然后用二进制打开!当我们找到一些东西时,我们可以看到一些线索!猜测这部分是每一行的记录信息。我们插入了七条数据,我发现其中一个二进制字符串可以分成七对。我单独拿出来,按行划分。我们拆解第一行记录,第一行记录的表数据是这样的。1dibingfa2在ibd文件中是这样的。0800000010002480000001000000000A07A70000011B0110646962696E67666180000002这串数据是什么意思?由于本文只能参考官方Documentation,这里先看一下Innodb的行格式。https://dev.mysql.com/doc/refman/5.7/en/innodb-row-format.html看这部分(我们的rowformat是DYNAMIC类型,但是参考COMPACT类型的描述也是可以的,因为差不多了,以后再说吧):看到这么长的列表不要害怕,慢慢来吧,别着急。我可能没有资格评论这份文件,但我个人觉得它写得不好。废话一大堆,没有解释格式长什么样,每个字节是什么意思。但它可能不会那样工作。一份好的官方文档应该能够把每一个字节、每一位都解释清楚,但是这个文档不好,还是找更接近一手资料的源码吧。找到了源码,还是很清楚的。每行记录的磁盘数据格式都写在注释里了,很棒,不用看代码了。然后粘贴刚才的第一行记录。0800000010002480000001000000000A07A70000011B0110646962696E67666180000002看一看,第一部分。|数据的最后一个非空可变长度字段的长度......|...|数据的第一个可变长度字段的长度|这部分是变长字段列表,就是依次记录所有变长字段字段的长度,由于我们只有一个变长字段的名字varchar(10),所以是08,和我们存储的“dibingfa”刚好是8个字节,没错。如果不止一个,显然,就这样保存它们。错误的!应该这样存储,即倒序存储。具体原因后面会讲到。好的,这就是第一个字节08的意思。再向下。|SQL-null标志(每个可空字段1位),填充到完整字节|第二个结构称为NULl值列表,它使用1位表示NULL值。填充一个字节,下一个字节为00,我们看到我们的记录中确实没有NULL值,没错。具体来说,也是倒序存储的。继续。|4位用于删除标记一条记录,按字母顺序标记一条预定义的最小记录||4位给出该记录拥有的记录数(该术语在page0page.h中解释)||13位给出索引页堆中此记录的顺序号||3位记录类型:000=conventional,001=nodepointer(insideB-tree),010=infimum,011=supremum,1xx=reserved||记录页|ORIGIN中下一条记录的相对指针的两个字节,这五个字节很乱,放在一起称为记录头信息,0000100024,表示删除状态,记录类型,下一条记录的相对位置等等。这一大堆先放着吧,因为涉及到很多额外的知识。继续阅读。|第一个数据字段|...|lastfieldofdata|其余都是具体的列数据,从第一列到最后一列。第一列是ID列,是INT类型的1,占四个字节80000001。开头的80是因为正数必须以1开头,这是mysql规定的。0x80的二进制值为10000000,所以这也是正确的。第二列是name列,是一个varchar类型的字符串,比如“dibingfa”。但它不符合以下内容。这是怎么回事?还记得每行mysql记录都有几个隐藏列,rowid,事务ID,回滚指针吗?没错,就是他们。其中,因为有主键,所以rowid是不存在的。也可以说,第一列要么是mysql给我们生成的一个6字节的rowid,要么是用户自定义的主键或者其他Uniquekey,首选用户自定义的key。允许。让我们一起来看看这五个栏目。(三个隐藏列,我们定义了两个列)主键ID:80000001事务ID:000000000A07回滚指针:A70000011B0110name列(dibingfa):646962696E676661agecolumn(2):80000002age列和刚才说的一样,mysql会在正数前面加1,所以age为2,80000002存盘。事务ID和回滚指针涉及到事务、隔离级别、MVCC等知识点,一大堆套路,这里就不展开了。总结了行记录格式的整体结构。整条行记录的格式称为mysql的行记录格式,ROW_FORMAT。这个ROW_FORMAT可以有不同的值,分别代表存储这一行记录的不同数据结构,它的枚举记录在remOtypes.h文件中。/**InnodbrowtypesareasubsetoftheMySQLglobalenumrow_type.Theyaremadeintotheirownenumsothatswitchstatementscanaccountforeachofthem.*/enumrec_format_enum{REC_FORMAT_REDUNDANT=0,/*!
