InnoDB存储引擎和大多数数据库一样,记录是以行的形式存储的。也就是说页中保存着表中一行行的数据。在InnoDB 1.0.x版本之前,InnoDB存储引擎
提供了Compact和Redundant两种格式来存放行记录数据。
Redundant格式是为了兼容之前版本而保留的,如果阅读过InnoDB的源代码,用户会发现源代码中是用PHYSICAL RECORD(NEW STYLE)和PHYSICAL RECORD(OLD STYLE)来区分两种格式。
在MySql 5.1版本中,默认设置为Compact行格式。用户可以通过下面命令查看表使用的行格式,其中row_format属性表示当前所使用的行记录结构类型。

mysql> show table status like 'test'\G
*************************** 1. row ***************************
           Name: test
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           Rows: 1Avg_row_length: 16384
    Data_length: 16384
Max_data_length: 0Index_length: 0
      Data_free: 5242880Auto_increment: NULL
    Create_time: 2016-07-20 16:13:43
    Update_time: NULL
     Check_time: NULL
      Collation: utf8_general_ci
       Checksum: NULLCreate_options:
        Comment:
1 row in set (0.00 sec)

可以看到test表是Compact的行格式。数据库实例的作用之一就是读取页中存放的行记录。如果用户自己知道页中行记录的组织规则,也可以自行通过编写工具的方式来读取其中的记录。

1、Compact行记录格式

Compact行记录是在MySql 5.0中引入的,其设计目标是高效地存储数据。简单来说,一个页中存放的行数据越多,其性能就越高。下面是Compact行记录的存储方式:

Compact行记录格式的首部是一个非NULL变长字段长度列表,并且其是按照列的顺序逆序放置的,其长度是:
(1)若列的长度小于255字节,用1字节表示;
(2)若大于255个字节,用2字节表示;

变长字段的长度最大不可以超过2字节,这是因为在MySql数据中varchar类型的最大长度限制为65535。
变长字段之后的第二部分是NULL标志位,该位指示了该行数据中是否有NULL值,有则用1表示。该部分所占的字节应该为1字节。
接下来的部分是记录头信息(record header),固定占用5字节(40位),每位的含义如下图所示:

最后的部分是实际存储每个列的数据。NULL不占该部分任何空间,即NULL除了占有NULL标志位,实际存储不占有任何空间。
另外,每行数据除了用户定义的列外,还有两个隐藏列,事务ID列和回滚指针列,分别为6字节和7字节的大小。若InnoDB表没有定义主键,每行还会增加一个6字节的rowid列。

接下来用一个具体示例来分析Compact行记录的内部结构:

mysql> create table mytest(-> t1 varchar(10),
    -> t2 varchar(10),
    -> t3 char(10),
    -> t4 varchar(10)
    -> )engine=innodb charset=latin1 row_format=compact;
Query OK, 0 rows affected (0.09 sec)

下面插入数据:

mysql> insert into mytest values('a','bb','bb','ccc');
Query OK, 1 row affected (0.02 sec)mysql> insert into mytest values('d', 'ee', 'ee', 'fff');
Query OK, 1 row affected (0.01 sec)mysql> insert into mytest values('d', NULL, NULL, 'fff');
Query OK, 1 row affected (0.01 sec)

查询表中的数据:

mysql> select * from mytest\G
*************************** 1. row ***************************
t1: a
t2: bb
t3: bb
t4: ccc
*************************** 2. row ***************************
t1: d
t2: ee
t3: ee
t4: fff
*************************** 3. row ***************************
t1: d
t2: NULL
t3: NULL
t4: fff
3 rows in set (0.00 sec)

如果启用了参数innodb_file_per_table,则打开表空间文件mytest.ibd。否则,打开默认的共享表空间文件ibdata1,本文中没有启用参数innodb_file_per_table,所有打开文件ibdata1.

在Linux下,首先找到ibdata1文件的位置。

root@TryHard:~# find / -name ibdata1;
/var/lib/mysql/ibdata1

然后,将二进制文件ibdata1重定向为mytest.txt文件,然后打开mytest.txt文件。

root@TryHard:~# hexdump -C -v /var/lib/mysql/ibdata1 > mytest.txt

找到如下图所示的内容:

下面是对第一行内容进行分析:
03 02 01 是变长字段长度列表,t1,t2,t3字段是变长字段,逆序显示这三个字段的长度。插入的数据中t3的值为3个字节,t2的值为2个字节,t1的值为1个字节。
00 是NULL标志位,为0表示第一行没有NULL值。
00 00 10 00 2c 是Record Header,固定5字节长度。
00 00 00 00 09 00 是InnoDB自动创建的rowID,6字节长度。
两个隐藏列,事务ID列和回滚指针列,分别为6字节和7字节的大小。
00 00 00 00 1e 09 是事务ID列,6字节长度。
8a 00 00 01 3d 01 10 是回滚指针列,7字节长度。
61是t1列的数据’a’;
62 62是t2列的数据’bb’;
62 62 20 20 20 20 20 20 20 是t3列的数据’bb’,t2是固定长度char字段,如果未用完其占用的长度空间时,会用0x20来进行填充。
63 63 63是t4列的数据’ccc’;

Record Header的值是00 00 10 00 2c,最后两个字节是00 2c,这两个字节代表next_recorder,0x2c代表下一个记录的偏移量,即当前记录的位置加上偏移量0x2c就是下一条记录的起始位置。所以InnoDB存储引擎在页内部是通过一种链表的结构来串连各个行记录的。

第二行和第一行类似 。
下面是第三行的内容:
03 01 变长字段长度列表,逆序;t1长度为1,t4长度为3,t2和t3为NULL。
06 NULL标志位,第三行有NULL值。
00 00 20 ff 98 是Record Header;
00 00 00 00 09 02是RowId,6个字节;
00 00 00 00 1e 0b是事务ID列,6个字节;
8c 00 00 02 1d 01 10是回滚指针列,7个字节。
64是t1字段的值’d’;
66 66 66是t4字段的值’f’;

第三行有NULL值,因为NULL标志位不再是00而是06,转换成二进制00000110,为1的值表示第2列和第3列的数据为NULL。其后存储列数据的部分,没有存储NULL列,而只存储了第1列和第4列的非NULL的值,这也说明了,不管是char还是varchar类型,在compact格式下NULL值都不占用任何存储空间

2、Redundant行记录格式

Redundant是MySql 5.0版本之前InnoDB的行记录存储方式,MySql 5.0支持Redundant是为了兼容之前版本的页格式。Redundant行记录采用下图所示的方式存储。

Redundant行记录格式的首部是一个字段长度偏移列表,同样是安装列的顺序逆序存放的。若列的长度小于255字节,用1字节表示;若大于255字节,用2字节表示。第二个部分为记录头信息(record header),不同于Compact行记录格式,Redundant行记录格式的记录头占用6字节,每位的含义如下图所示:

其中,n_fields的值代表一行中列的数量,占用10位,所以能表示的最大值为1023,这也解释了为什么MySql数据库一行支持的最多的列数量为1023。另一个需要注意的值为1byte_offs_flag,该值定义了偏移列表占用1字节还是2字节。
最后部分就是实际存储的每个列的数据了。

创建一个Redundant表mytest2:

mysql> create table mytest2  engine=innodb row_format=redundant  as select * from mytest;
Query OK, 3 rows affected (0.04 sec)
Records: 3  Duplicates: 0  Warnings: 0

查看表mytest2信息:

mysql> show table status like 'mytest2'\G
*************************** 1. row ***************************
           Name: mytest2
         Engine: InnoDB
        Version: 10
     Row_format: Redundant
           Rows: 3Avg_row_length: 5461
    Data_length: 16384
Max_data_length: 0Index_length: 0
      Data_free: 5242880Auto_increment: NULL
    Create_time: 2017-03-20 15:46:57
    Update_time: NULL
     Check_time: NULL
      Collation: utf8_general_ci
       Checksum: NULLCreate_options: row_format=REDUNDANT
        Comment:
1 row in set (0.00 sec)

查看数据:

mysql> select * from mytest2\G
*************************** 1. row ***************************
t1: a
t2: bb
t3: bb
t4: ccc
*************************** 2. row ***************************
t1: d
t2: ee
t3: ee
t4: fff
*************************** 3. row ***************************
t1: d
t2: NULL
t3: NULL
t4: fff
3 rows in set (0.00 sec)

将二进制文件ibdata1重定向为mytest.txt文件,然后打开mytest.txt文件。

root@TryHard:~# hexdump -C -v /var/lib/mysql/ibdata1 > mytest.txt

第一行数据:

23 20 16 14 13 0c 06 是长度偏移列表,逆序。
00 00 10 0f 00 ba 是Record Header,固定6个字节;
00 00 00 00 09 0c 是rowId列,6个字节。
00 00 00 00 1e 1b 是事务ID列,6个字节。
9a 00 00 01 4b 01 10 是回滚指针列,7个字节。

61 是t1列数据’a’;
62 62是t2列数据’bb’;
62 62 20 20 20 20 20 20 20 20 是t3列数据’bb’, char类型;
63 63 63是t4列数据’ccc’;
23 20 16 14 13 0c 06 逆序为06,0c,13,14,16,20,23,分别代表第一列长度为6,第二列长度为6(6+6 = 0x0c),第三列长度为7(6+6+7=0x13),第四列长度为1(6+6+7+1=0x14),第五列长度为2(6+6+7+1+2=0x16),第六列长度为10(6+6+7+1+2+10=0x20),第七列长度为3(6+6+7+1+2+10+3=0x23).

第1列指的是rowID列,第2列指的是事务ID列,第3列指的是回滚指针列,第4列指的是t1,……
在接下来的记录头信息(record Header)中应该注意到48位中的第22-32位,为0000000111,表示表共有7个列(包含了隐藏的3列),接下来的第33位为1,大表偏移列表为一个字节。

后面的信息就是实际存放的数据。

第三行数据:

21 9e 94 14 13 0c 06 长度偏移列表,逆序;
00 00 20 0f 00 74 是Record Header,固定长度为6字节;
00 00 00 00 09 0e是RowId列;
00 00 00 00 1e 1b 是事务id,6个字节;
9a 00 00 01 4b 01 2e 是回滚指针列,7 个字节;
64是列t1的数据’d’;
t2类型是varchar,并且值为NULL,所以t2的值不占用存储空间。
00 00 00 00 00 00 00 00 00 00 是t3的数据NULL,t3是char类型;
66 66 66是列t4的数据’fff’;

长度偏移列表逆序之后得到 06, 0c, 13, 14, 94, 9e, 21,前4个值都好理解。
第5个NULL值变为了94(该列是t2字段),第6个char类型的NULL值为9e(94+10=0x9e)(该列是t3的字段),之后的21大表(14+3 = 0x21)。可以看出对于varchar类型的NULL值,Redundant行记录格式同样不占用任何存储空间,而char类型的NULL值需要占用空间。

当前表mytest2的字符集为Latin1,每个字符最多只占用1个字节。若用户将表mytest2的字符集转换为utf8,第三列char固定长度类型不再是只占用10字节了,而是10*3 = 30字节。

InnoDB行记录格式相关推荐

  1. mysql compact_[MySQL]InnoDB行格式剖析_MySQL - compact

    ... $this->layout->main=View::make('dash')->nest('content','comments.list',compact('comment ...

  2. MySQL数据库—InnoDB行存储格式

    目录 一.InnoDB支持的行存储格式 二.Compact记录格式 1.变长字段长度列表 (1)对varchar类型字段长度的存储 (2)对char类型字段长度的存储 2.NULL标志位 3.记录头信 ...

  3. MySQL · 引擎分析 · InnoDB行锁分析

    前言 理解InnoDB行锁,分析一条SQL语句会加什么样的行锁,会锁住哪些数据范围对业务SQL设计和分析线上死锁问题都会有很大帮助.对于InnoDB的行锁,已经有多篇月报进行了介绍,这里笔者借鉴前面月 ...

  4. InnoDB行格式(compact,redundant)对照

    InnoDB行格式分两种格式(COMPACT,redundant)默觉得COMPACT compact的存储格式为 首部为一个非NULL的变长字段长度列表,并且是依照列的顺序逆序放置的,当列的长度小于 ...

  5. innodb行锁理解

    innodb行锁的一些认识 InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的. InnoDB这种行锁实现特点意味着:只有通过索 ...

  6. 9、 InnoDB行锁

    在 MySQL 中,InnoDB 行锁通过给索引上的索引项加锁来实现,如果没有索引,InnoDB 将通过隐藏的聚簇索引来对记录加锁. InnoDB 支持 3 种行锁定方式: 行锁(Record Loc ...

  7. MySQL高级 - 锁 - InnoDB行锁 - 争用情况查看

    InnoDB 行锁争用情况 show status like 'innodb_row_lock%'; Innodb_row_lock_current_waits: 当前正在等待锁定的数量Innodb_ ...

  8. mysql 表 页 行_Mysql之InnoDB行格式、数据页结构

    Mysql架构图 存储引擎负责对表中的数据的进行读取和写入,常用的存储引擎有InnoDB.MyISAM.Memory等,不同的存储引擎有自己的特性,数据在不同存储引擎中存放的格式也是不同的,比如Mem ...

  9. InnoDB 行格式

    InnoDB表 的默认行格式由innodb_default_row_format 变量定义 ,其默认值为DYNAMIC.当ROW_FORMATtable 选项未明确定义或ROW_FORMAT=DEFA ...

  10. MySQL数据库锁机制之MyISAM引擎表锁和InnoDB行锁详解

    MySQL中的锁概念 Mysql中不同的存储引擎支持不同的锁机制.比如MyISAM和MEMORY存储引擎采用的表级锁,BDB采用的是页面锁,也支持表级锁,InnoDB存储引擎既支持行级锁,也支持表级锁 ...

最新文章

  1. Android规范发展
  2. 将本地的MS SQL Server数据导入到远程服务器上
  3. Windows10完美安装VMTK(血管建模工具包)
  4. HDU - 3068 最长回文(manacher)
  5. php 格式化 sub,PHP DateTime sub()用法及代码示例
  6. 美国知名华人学者陈刚被捕,他出身贫寒,是一个不折不扣的工作狂
  7. 小度智能屏X10正式发布:10.1英寸超大屏 售价999元
  8. web memory
  9. airpods pro连接安卓声音小_安卓手机用 AirPods ?你需要这个 App
  10. Navicat for MySQL 破解版
  11. 发动机冒黑烟_发动机冒黑烟的原因和解决方法
  12. mysql计算个税_2019年个人所得税计算函数
  13. 【重拾FPGA】读锆石科技硬件语法篇有感
  14. 操作系统重要知识清单:一起来搞懂进程呀!!
  15. 【论文】开放域段落检索的句子感知对比学习
  16. 不限速开源的下载工具:Persepolis Download Manager
  17. IPv6基础介绍及常用命令盘点
  18. sql server 2008 R2 与 sql server 2012 下载地址(包括x86、x64)
  19. displayl:flex布局
  20. OnMeasure()

热门文章

  1. QProgressDialog setValue过快导致死机问题记录
  2. javascript 在线文本编辑器
  3. 透明状态栏的实现(Activity里有5个fragment,fragment顶部有图片有纯色的actionbar)
  4. No code “EPSG:4326“ from authority “EPSG“
  5. 基于Redis的推荐系统开发
  6. 运动目标跟踪(十七)--一些跟踪算法简述及跟踪牛人资料整理
  7. 日语中的-简体与敬体
  8. linux ubuntu 联网问题
  9. java hevc和heif_什么是HEVC和HEIF?有什么优缺点?
  10. JAVA101本土精选,Java101系列文章