赵玉伟的博客

innodb逻辑存储--表、段、区、页

概述

简要说一下innodb的逻辑存储。对于这一部分内容, 网上的资料相对较少,同时对于我们理解数据的存储格式又比较重要, 后来,有幸看到在《Mysql技术内幕 InnoDB存储引擎》这本书的第四章,专门做了讲解, 讲解的角度也基本面向mysql的使用者,学习的同时, 我在网上也搜集了部分资料。 本文做下梳理,但由于脱离了源码分析,只能算是以管窥豹,仅仅作为知识点的摘要。

数据库的逻辑结构,从oracle到mysql,基本上是按照以下逻辑划分的, 从大到小依次是: 表空间(Tablespace)、段(Segment)、区(Extent)、页(Page)、行(Row)。其中, 页在Oracle中也叫 块(Block)。先盗一张网上的图, 有一个感官认知,然后依次说明:

存储目录

表空间是数据存储的最高层,所有的数据都存在表空间中。
既然表空间是用来存数据的, 而在操作系统层面, 数据是被放到一个一个文件中的, 那么,表空间就是某一个文件或者某几个文件的集合。 mysql把这些文件放在哪里呢?可以通过以下方式寻找:
第一步,找mysql的配置文件:

1
mysql --help | grep my.cnf


以上的目录, 会有一个大小关系的优先级, 以最小的为准。
打开文件my.cnf, datadir = /data/mysql 这一项配置就是mysql存储数据的地方。

表空间

mysql 对表空间做了区分,分为独立表空间和共享表空间。

共享表空间: 所有的数据保存在一个表空间中, 一个表空间被所有的表共享, 这个表空间的起始文件名为 ibdata1, 这个文件达到一定大小后, 可以扩展成ibdata2、ibdata3、…… ibdata{n} 多个文件。

独立表空间: 每一个表的数据保存在独立的表空间中, 一个表空间被某一张表独占, .frm为表的描述文件, .ibd为表的数据文件。这些文件的位置一般放在 {datadir}/{database}/ 下。

优缺点:

共享表空间:

1、由于数据比较集中,方便管理(个人觉得这个优点有点牵强,实际应用中,很少对某个数据库进行跨机器的硬拷贝, 方便何在? 而且几个文件对于我们来说, 就是一个黑盒子,什么也看不见, 我们又怎么管理呢?);
2、drop掉某张表,或者大量删除数据后, 会产生大量的空隙,也就是碎片, 对空间来说比较浪费;
3、所有的操作落到一个或者几个文件中,可能会影响性能
(本人对于这个缺点持怀疑态度, 因为上层的应用落到操作系统开始, 之后,再到磁盘的io, 已经没有了表空间这个概念了, 所以影响性能的可能仅仅也是由于文件中被删除了大量数据, 磁盘中产生了碎片,从而影响磁头的寻址时间, 但是这个性能的开销, 数据量不是很大的话, 我认为可以忽略)。

独立表空间:

1、数据以表的粒度单独存储,每个表的数据由自己的文件保持,由于粒度从库变到表,我们管理的粒度变小了很多, 可以单独对某张表的数据操作, 我认为这样反而好管理一些。
2、删除数据或者把某个表drop掉之后, 可以对这些空间进行回收,这个优点比较突出, 我们可以尽可能多的使用磁盘的空间。

如何选择

所以, 如果只是随便用用,业务量不是很大,可以选择共享表空间; 如果数据量较大,并且服务需要作为一个严谨的工程提供服务, 那就选择独立表空间, 我接触过的应用,表空间几乎都是独立的。 同时, 这个设置在数据库刚刚配置的时候就要设置好, 如果需要切换, 只能停服务, 重新配置,再把数据导入,相当于又重新初始化了一遍。

注意: 当配置成独立表空间之后, 每张表的表空间内只存放数据、 索引、 缓冲页; 回滚信息(undolog)、插入缓冲索引页、事务信息、二次写缓冲还存放在原先的表空间中,所以ibddata1仍旧会不停的增大。

相关操作

查看变量的sql:

1
show variables like '%innodb_%'; 查看innodb的变量列表

查看表大小的sql

1
2
use information_schema;
select * from TABLES where TABLE_NAME = 'product';

TABLE_SCHEMA : 数据库名
TABLE_NAME:表名
ENGINE:所使用的存储引擎
TABLES_ROWS:记录数
DATA_LENGTH:数据大小
INDEX_LENGTH:索引大小

表空间的数据, 是以段为单位进行组织的。 为什么要分段? 因为段是按照功能来划分的。 常见的有数据段、 索引段、 回滚段等。

段中的数据, 是以区为单位进行组织的。每个区的大小为固定的1M。 由于页的大小默认为16K,这个值的大小是放在变量innodb_page_size 中的。
| innodb_page_size | 16384 |
所以, 一个区中可以包含96(1024K/64K)个页。区是innodb存储引擎向磁盘申请的最小单位。 由于区中包含的是页, 为了保证页的连续性(磁盘读取连续的数据效率是比较高的, 因为磁头不需要来回跳转的寻址,而且操作系统有预读功能), innodb一次向磁盘申请4~5个区。

那么, 既然区是向磁盘申请空间的最小单位, 而且区的大小固定是1M,是不是每个文件的最小值为1M呢?

这里的文件主要是指我们放数据的 .ibd文件。答案是否定的, 这是因为innodb为了照顾数据量比较小的段,或者一些小表,由于他们的数据存储比较小,一次分配1M的空间是比较浪费的, 所以, innodb为了提高磁盘的使用率, 在存储数据时, 首先会用32个碎片页存储,这些碎片页使用完之后, 才按照块进行申请。

页是磁盘管理的最小单位, innodb的默认页大小为16k,这个值可以修改, 值得大小需要时2的n次方。 虽然可以修改, 但是常规情况下, 这个值是不需要动的。 既然是磁盘管理的最小单位, 那么这个值便和磁盘的IO有关。一次IO可以取出整数个页的数据。

页的最常用的类型就是数据存储的B+数, 一个节点的大小,就是一个页的大小。这个节点既包括索引节点, 也包括作为叶子节点的数据节点。所以,一个16K的节点, 用于存储递增的主键, 是足够大的,这就是为什么聚簇索引树的高度在4层左右的原因。
其他的类型包括:
undo页(undo log),这是innodb实现mvcc的基础。
事务数据页

还有其他的像 系统页、 二进制大对象页等,对于mysql的使用者来说, 我认为知道其存在就可以了。 另外, 在16K的页中,每个字节的作用, 也都有划分, 有兴趣的朋友可以看下我文章末尾参考的链接。

总结

以上对数据库的逻辑结构做了一个大致说明,我们在了解了innodb的存储结构, 由于数据库是和磁盘打交道的, 再简述下IOPS。
mysql是一个比较细腻的系统,所以,单纯说IO是比较粗糙的说法, 刨去是否读取连续的数据, 或者读取的是大块的数据还是小块的数据, 以及数据在磁盘上的分布, 至少要圈定在某一个层面, 因为从应用、到操作系统、到IO指令,越往下IO越密集。

IOPS,即IO peer second,也就是每秒的IO次数。 这个数值和磁盘的转速以及制造商是有关系的,这是个大约值。 不过下面几个数据最好要记住(可以参考我文章末尾的链接):
7200 rpm的磁盘 IOPS = 1000 / (9 + 4.17) = 76 IOPS (大约在75 ~ 100之间)
10000 rpm的磁盘IOPS = 1000 / (6+ 3) = 111 IOPS (大约在100 ~ 150之间)
15000 rpm的磁盘IOPS = 1000 / (4 + 2) = 166 IOPS(大约在175 ~ 200之间)

有一条比较有用的命令, show status 可以查看当前mysql的状态。

参考

逻辑结构相关
http://blog.csdn.net/idber/article/details/8087473

http://blog.csdn.net/generalyy0/article/details/8242419

http://blog.csdn.net/lijingkuan/article/details/50741364

硬盘IO相关
http://blog.csdn.net/hanchengxi/article/details/19089589

http://elf8848.iteye.com/blog/1731301

http://blog.csdn.net/hguisu/article/details/7408047

https://community.emc.com/docs/DOC-29186