《MySQL系列-InnoDB引擎25》表-InnoDB逻辑存储结构

Posted DATA数据猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《MySQL系列-InnoDB引擎25》表-InnoDB逻辑存储结构相关的知识,希望对你有一定的参考价值。

InnoDB逻辑存储结构

  从InnoDB存储引擎的逻辑存储结构看,所有数据都被逻辑地存放在一个空间中,称之为表空间(tablespace)。表空间又由段(segment)、区(extent)、页(page)组成。页在一些文档中有时也称为块(block),InnoDB存储引擎的逻辑存储结构大致如下。

1 表空间

  表空间可以看做是InnoDB存储引擎逻辑结构的最高层,所有的数据都存放在表空间中。在默认情况下InnoDB存储引擎有一个共享表空间ibdata1,即所有数据都存放在这个表空间内。如果用户启用了参数innodb_file_per_table,则每张表的数据可以单独放到一个表空间内。

  如果启用了innodb_file_per_table的参数,需要注意的是每张表的表空间内存放的只是数据、索引和插入缓存Bitmap页,其他类的数据,如回滚(undo)信息,插入缓存索引页、系统事务信息,二次写缓存(Double write buffer)等还是存放在原来的共享表空间内。

  可以使用py_innodb_page_info小工具,来查看表空间中各页的类型和信息。在使用时需要注意的是,总共有三个文件,分别是include.pymylib.pypy_innodb_page_info.py,放在同一目录即可。

GitHub下载

# 1.python文件
[root@zxy_master py_innodb_page_type]# ll
total 12
-rw-r--r-- 1 root root 1257 Feb 23 10:06 include.py
-rw-r--r-- 1 root root 2179 Feb 23 10:07 mylib.py
-rw-r--r-- 1 root root  234 Feb 23 09:59 py_innodb_page_info.py

# 2.表空间统计查看
[root@zxy_master py_innodb_page_type]# python py_innodb_page_info.py /var/lib/mysql/ibdata1
# 总页数
Total number of page: 4864:
# 插入缓存
Insert Buffer Bitmap: 1
# 系统页
System Page: 123
# 事务系统页
Transaction system Page: 2
# 可用页
Freshly Allocated Page: 4326
# Undo页
Undo Log Page: 288
# segment节点页
File Segment inode: 8
# 数据页
B-tree Node: 114
#  表空间标题页
File Space Header: 2

# 3.详细表空间内容查看
[root@zxy_master py_innodb_page_type]# python -v py_innodb_page_info.py /var/lib/mysql/ibdata1
......

2 段

  表空间是由各个段组成,常见的段有数据段、索引段、回滚段等。

  其中,leaf node segment是B+树的叶子节点,即数据段。Non-leaf node segment是B+树的非索引节点,即索引段。Roolback segment是回滚段。

  在InnoDB引擎中,对段的管理都是由引擎自身所完成的,DBA不能也没有必要对其进行控制。这和Oracle对数据库中自动段的管理(ASSM)类似,从一定程度上简化了DBA对段的管理。

3 区

  区由连续页组成的空间,在任何情况下每个区的大小都为1MB。为保证页的连续性,InnoDB存储引擎一次从磁盘申请4-5个区。在默认情况下,InnoDB存储引擎的页大小为16KB,即一个区共有64个连续的页。

  在InnoDB 1.2.x版本新增参数innodb_page_size后,通过该参数可以将默认页的大小设置为4K、8K,但是页中的数据库不是压缩。这时区中的页的数量通用页为256、128。总之,不论页的大小怎么变化,区的大小总是1M。

mysql> show variables like 'innodb_page_size';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| innodb_page_size | 16384 |
+------------------+-------+
1 row in set (0.00 sec)

  当启用参数innodb_file_per_table后,创建的表默认是96KB。

这时就引起了思考?

  一个区有64个页,一个页16KB,那么创建的表至少应该是1MB才对?为什么这里创建的表才96KB。

# 1.为方便测试,t2字段类型为varchar(7000),能保证一个页最多存放2条记录
mysql> create table page_test(
    -> t1 int not null auto_increment,
    -> t2 varchar(7000),
    -> primary key (t1)
    -> ) engine=innodb;
Query OK, 0 rows affected (0.04 sec)

# 2.通过查看表空间文件,可以发现初始表空间大小默认96KB
mysql> system ls -lh /var/lib/mysql/zxy/page_test.ibd
-rw-r----- 1 mysql mysql 96K Feb 23 11:00 /var/lib/mysql/zxy/page_test.ibd

  其实是每个段开始时,先用32个页大小的碎片页(fragement page)来存放数据,在使用完这些页后才开始64个连续页的申请。这样做的目的是对于一些小表,或者是undo这类的段,可以在开始申请较少的空间,节省磁盘容量的开销。

测试一:插入两条数据,表空间大小、B+树变化

结论1:经过测试发现表大小未发生变化

# 1.插入第一条
mysql> insert into page_test select null,repeat('a',7000);
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

# 2.插入第二条
mysql> insert into page_test select null,repeat('a',7000);
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

# 3.查看大小
mysql> system ls -lh /var/lib/mysql/zxy/page_test.ibd
-rw-r----- 1 mysql mysql 96K Feb 23 11:26 /var/lib/mysql/zxy/page_test.ibd

结论2:

  通过py_innodb_page_info.py工具,参数-v查看表空间详情。可以看到page offset是3的页,这个是数据页。page level表示所在索引层,0表示叶子节点。因为两条数据未满16KB,所以当前所有记录都在一个页中,因此没有非叶节点。但是如果再插入一条数据,就会产生非叶节点。

[root@zxy_master py_innodb_page_type]# python py_innodb_page_info.py  -v /var/lib/mysql/zxy/page_test.ibd
page offset 00000000, page type <File Space Header>
page offset 00000001, page type <Insert Buffer Bitmap>
page offset 00000002, page type <File Segment inode>
page offset 00000003, page type <B-tree Node>, page level <0000>
page offset 00000000, page type <Freshly Allocated Page>
page offset 00000000, page type <Freshly Allocated Page>
Total number of page: 6:
Freshly Allocated Page: 2
Insert Buffer Bitmap: 1
File Space Header: 1
B-tree Node: 1
File Segment inode: 1

测试二:再次插入一条数据后,表空间大小、B+树变化

结论1:经过测试发现表大小未发生变化

mysql> insert into page_test select null,repeat('a',7000);
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> system ls -lh /var/lib/mysql/zxy/page_test.ibd
-rw-r----- 1 mysql mysql 96K Feb 23 11:34 /var/lib/mysql/zxy/page_test.ibd

结论2:可以看到page offset为3的页的page level由之前的0变为了1,这时虽然新增的记录导致了B+树的分裂操作,但这个页的类型还是B-tree Node。

[root@zxy_master py_innodb_page_type]# python py_innodb_page_info.py  -v /var/lib/mysql/zxy/page_test.ibd
page offset 00000000, page type <File Space Header>
page offset 00000001, page type <Insert Buffer Bitmap>
page offset 00000002, page type <File Segment inode>
page offset 00000003, page type <B-tree Node>, page level <0001>
page offset 00000004, page type <B-tree Node>, page level <0000>
page offset 00000005, page type <B-tree Node>, page level <0000>
Total number of page: 6:
Insert Buffer Bitmap: 1
File Space Header: 1
B-tree Node: 3
File Segment inode: 1

测试三:再次插入60条数据后,表空间大小、B+树变化

结论1:表空间大小变化

mysql> DELIMITER &&

mysql> create procedure load_t1(count int unsigned)
    -> begin
    -> declare s int unsigned default 1;
    -> declare c varchar(7000) default repeat('a',7000);
    -> while s <= count DO
    -> insert into page_test select null,c;
    -> set s = s+1;
    -> end while;
    -> end;
    -> &&
Query OK, 0 rows affected (0.01 sec)

mysql> delimiter ;

mysql> call load_t1(60);
Query OK, 1 row affected (0.67 sec)

# 可以看到现在有63条数据后,表空间的大小还是小于1MB,即表示数据空间的申请还是通过碎片页,而不是通过64个连续页的区。
mysql> system ls -lh /var/lib/mysql/zxy/page_test.ibd;
-rw-r----- 1 mysql mysql 592K Feb 23 13:50 /var/lib/mysql/zxy/page_test.ibd

结论2:

  可以观察到,B-tree Node页一共有33个,除去一个page level为1的非叶节点,一共有32个page level为0的页,也就是说,对于数据段,已经有32个碎片页了。之后如果再申请空间,肯定就是按照64个页大小进行增长了。

[root@zxy_master py_innodb_page_type]# python py_innodb_page_info.py  -v /var/lib/mysql/zxy/page_test.ibd
page offset 00000000, page type <File Space Header>
page offset 00000001, page type <Insert Buffer Bitmap>
page offset 00000002, page type <File Segment inode>
page offset 00000003, page type <B-tree Node>, page level <0001>
page offset 00000004, page type <B-tree Node>, page level <0000>
page offset 00000005, page type <B-tree Node>, page level <0000>
page offset 00000006, page type <B-tree Node>, page level <0000>
page offset 00000007, page type <B-tree Node>, page level <0000>
page offset 00000008, page type <B-tree Node>, page level <0000>
page offset 00000009, page type <B-tree Node>, page level <0000>
page offset 0000000a, page type <B-tree Node>, page level <0000>
page offset 0000000b, page type <B-tree Node>, page level <0000>
page offset 0000000c, page type <B-tree Node>, page level <0000>
page offset 0000000d, page type <B-tree Node>, page level <0000>
page offset 0000000e, page type <B-tree Node>, page level <0000>
page offset 0000000f, page type <B-tree Node>, page level <0000>
page offset 00000010, page type <B-tree Node>, page level <0000>
page offset 00000011, page type <B-tree Node>, page level <0000>
page offset 00000012, page type <B-tree Node>, page level <0000>
page offset 00000013, page type <B-tree Node>, page level <0000>
page offset 00000014, page type <B-tree Node>, page level <0000>
page offset 00000015, page type <B-tree Node>, page level <0000>
page offset 00000016, page type <B-tree Node>, page level <0000>
page offset 00000017, page type <B-tree Node>, page level <0000>
page offset 00000018, page type <B-tree Node>, page level <0000>
page offset 00000019, page type <B-tree Node>, page level <0000>
page offset 0000001a, page type <B-tree Node>, page level <0000>
page offset 0000001b, page type <B-tree Node>, page level <0000>
page offset 0000001c, page type <B-tree Node>, page level <0000>
page offset 0000001d, page type <B-tree Node>, page level <0000>
page offset 0000001e, page type <B-tree Node>, page level <0000>
page offset 0000001f, page type <B-tree Node>, page level <0000>
page offset 00000020, page type <B-tree Node>, page level <0000>
page offset 00000021, page type <B-tree Node>, page level <0000>
page offset 00000022, page type <B-tree Node>, page level <0000>
page offset 00000023, page type <B-tree Node>, page level <0000>
page offset 00000000, page type <Freshly Allocated Page>
Total number of page: 37:
Freshly Allocated Page: 1
Insert Buffer Bitmap: 1
File Space Header: 1
B-tree Node: 33
File Segment inode: 1

测试四:继续插入60条数据后,表空间大小、B+树变化

结论1:表空间大小增加明细

mysql> call load_t1(60);
Query OK, 1 row affected (0.68 sec)

mysql> system ls -lh /var/lib/mysql/zxy/page_test.ibd;
-rw-r----- 1 mysql mysql 9.0M Feb 23 13:59 /var/lib/mysql/zxy/page_test.ibd

结论2:出现较多freshly allocated page空闲页。

[root@zxy_master py_innodb_page_type]# python py_innodb_page_info.py  -v /var/lib/mysql/zxy/page_test.ibd
.......
Total number of page: 576:
Freshly Allocated Page: 510
Insert Buffer Bitmap: 1
File Space Header: 1
B-tree Node: 63
File Segment inode: 1

4 页

  同大多数数据库一样,InnoDB有页(Page)的概念(也可以称为块),页是InnoDB磁盘管理的最小单位。在InnoDB存储引擎中,默认每个页的大小为16KB。从InnoDB 1.2.x版本开始,可以通过参数innodb_page_size将页的大小设置为4K、8K、16K。若设置完成,则所有表中页的大小都为innodb_page_size,不可以对其进行再次修改。除非通过mysqldump导入和导出操作产生新的库。

在InnoDB存储引擎中,常见的页类型有:

  • 数据页(B-tree Node)
  • undo页 (undo log Page)
  • 系统页(System Page)
  • 事务数据页(Transcation system Page)
  • 插入缓存位图页(Insert Buffer Bitmap)
  • 插入缓存空闲列表页(Insert Buffer Free List)
  • 未压缩的二进制大对象页(Uncompressed BLOB Page)
  • 压缩的二进制大对象页(compressed BLOB Page)

5 行

  InnoDB存储引擎是面向列的,也就是说数据是按行存放的。每个页存放的行记录也是有硬性定义的饿,最多存放16KB/2-200行的记录。

  每页最少存储2行记录,用链表连接气力啊,否则会失去B+树的意义。数据大的行记录,如:大字符串、TEXT、BLOB对象,都是采用行溢出数据存储。不同的行格式、存储方式不同。

以上是关于《MySQL系列-InnoDB引擎25》表-InnoDB逻辑存储结构的主要内容,如果未能解决你的问题,请参考以下文章

innodb和myisam的区别

《MySQL系列-InnoDB引擎23》文件-InnoDB存储引擎文件-表空间文件

《MySQL系列-InnoDB引擎26》表-InnoDB行记录格式

《MySQL系列-InnoDB引擎24》表-索引组织表

《MySQL系列-InnoDB引擎27》表-文件格式

mysql数据库引擎