小言_互联网的博客

MySQL(InnoDB剖析):15---table之(表空间:段(segment)、区(extent)、页(page))

584人阅读  评论(0)

一、InnoDB逻辑存储结构

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

二、表空间

  • 表空间可以看做是InnoDB存储引擎逻辑结构的最高层所有的数据都存放在表空间中

默认表空间

  • 前面文章已经介绍了在默认情况下InnoDB有一个共享表空间ibdata1,即所有数据都存放在这个表空间内
show variables like 'innodb_data_file_path'\G;

 

innodb_file_per_table参数

  • 如果用户启用了innodb_file_per_table参数,则每张表内的数据可以单独放到一个表空间内(具体参阅前面文章的介绍:https://blog.csdn.net/qq_41453285/article/details/104115914
  • 如果使用此参数,需要注意的是每张表的表空间内存放的只是数据、索引和插入缓冲Bitmap页,其他类的数据,如回滚(undo)信息,插入缓冲索引页、系统事务信息,二次写缓冲等还是存放在原来的共享表空间内

共享表空间的大小问题

  • 上面通过innodb_file_per_table参数我们知道即使为每个表都是用单独的表空间,但是表的有些信息还是要存放到共享表空间内,因此共享表的空间会不断增加其大小
  • 下面进行一个演示案例,来观察共享表空间大小的增长过程
  • 先将innodb_dile_per_table参数设置为“NO”。然后观察一下默认的共享表空间文件的大小(为58M)

  • 接下来模拟产生undo的操作,set autocommit设置为0代表用户需要显式提交事务(下图中,在事务提交结束时并没有以对钙食物执行commit或rollback,因此会产生大量undo操作的update语句)。可以看到共享表空间的大小增加了

  • 如果对事务进行回滚操作,那么共享表空间的大小会不会缩减至原来的大小呢?,见下图

  • 答案是否,共享表空间的大小没有减小。虽然InnoDB不回回收这些空间,但是会自动判断这些undo信息是否还需要,如果不需要,则会将这些空间标记为可用空间,供下次undo使用
  • 当用户再次执行上述的update语句后,会发现共享表空间文件不会再增大了,因为其会利用之前的undo信息

Python脚本:查看表空间中各页的信息

  • 此脚本可以在GitHub进行下载:https://github.com/jameslcj/david-mysql-tools
  • 这个脚本用来查看表空间中各页的类型和信息,使用方法如下:
    • 共有83584个页。其中插入缓冲的空间列表有204个页、5467个可用页、38675个undo页、39233个数据页等

  • 用户可以通过-v选项来查看更详细的内容

三、表空间之“段”

  • 从上图可以看到表空间是由多个段组成的,常见的段有数据段、索引段、回滚段等
  • 因为前面介绍过InnoDB是索引组织的,因此数据即索引,索引即数据
    • 那么数据段就是B+树的叶子节点(Leaf node segment)
    • 索引段即为B+树的非索引节点(Non-leaf node segment)
    • 回滚段比较特殊,在后面介
  • 在InnoDB中,对段的管理都是有存储引擎自身完成的,DBA不能也没有必要对其进行空间。这和Oracle数据库中的自动段空间管理(ASSM)类似,从一定程度上简化了DBA对于段的管理

四、表空间之“区”

  • 区是由连续页组成的空间
  • 在任何情况下每个区的大小都为1MB为了保证区中页的连续性,InnoDB存储引擎一次从磁盘申请4~5个区

区中页的数量

  • 在默认情况下,InnoDB存储引擎页的大小为16KB,即一个区一共有64个连续的页
  • InnoDB 1.0.x开始引入压缩页,即每个页的大小可以通过参数KEY_BLOCK_SIZE设置为2K、4K、8K,因此每个区对应页的数量就应该为512、256、128
  • InnoDB 1.2.x开始新增了参数innodb_page_size,通过该参数可以将默认页的大小设置为4K、8K。但是页中的数据库不是压缩。这时区中页的数量同样也为256、128
  • 总之,不论页的大小怎么变化,区的大小总是为1M

区的申请方式(碎片页)

  • 当启用参数innodb_file_per_table后,创建的表默认大小是96KB。但是我们知道一个区就占用了1MB,那么为什么之后96KB呢?
  • 这是因为在每个段开始时,先用32个页大小的碎片页去存放数据,在使用完这些页之后才开始申请64个连续的页
  • 这样做的目的是:对于一些小表,或者是undo这类的段,可以在开始时申请较少的空间,节省磁盘容量的开销

区申请的演示案例

  • 下面通过一个很小的示例来显示InnoDB对于区的申请方式
  • 第一步:创建一个t1表,将col2字段设为VARCHAR(7000),这样就能保证一个页最多可以存放2条记录(因为一个页的大小为16KB*1024=16384bit)。然后通过ls命令查看到表空间默认大小为96KB

  • 第二步:接着向表中插入两条SQL语句,然后查看表空间的大小发现空间没有增加,并且这两条记录应该位于同一个页中

  • 第三步:此时再去使用上面的工具查看表空间,可以看到
    • page offset为3的页:这个就是数据页
    • page level:表示所在索引层,0表示叶子节点。因为当前所有记录都在一个页中,因此没有非叶子节点

  • 第四步:现在我们再插入一条记录,就会产生一个非叶节点:
    • page level:page offset为3的页的page level由之前的0变为了1,这时虽然新插入的记录导致B+树的分裂操作,但这个页的类型还是B-tree Node

  • 第五步:接着上述同样的操作,再插入60条记录,也就是说当前表t1中共有63条记录,32个页。为了导入的方便,在这之前先建立一个导入的存储过程
    • 可以看到,在导入了63条数据后,表空间的大小还是小于1MB,即表示数据空间的申请还是通过碎片页,而不是通过64个连续页的区

  • 第六步:再次查看表空间文件,可以观察到B-tree Node一共有33个,除去一个page level为1的非叶节点页,一共有32个page level为0的页,也就是说,对于数据库,已经有32个碎片页了。之后用户再申请空间,则表空间按连续64个页的大小开始增长了

  • 第七步:现在继续插入一条记录,之后看看表空间的大小。因为已经用完了32个碎片页,新的页回采用区的方式进行空间的申请

  • 第八步:现在再次分析表空间文件

五、表空间之“页”

  • 页有时候也称为块
  • 页是InnoDB磁盘管理的最小单位

innodb_page_size参数

  • 在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)
    • 事务数据页(Transaction system Page)
    • 插入缓冲位图页(Insert Buffer Bitmap)
    • 插入缓冲空闲列表页(Insert Buffer Free List)
    • 未压缩的二进制大对象页(Uncompressed BLOB Page)
    • 压缩的二进制大对象页(compressed BLOB Page)

六、行

  • InnoDB存储引擎是面向列的(row-oriented),也就说数据是按行进行存放的。每个页存放的记录也是硬性定义的,最多允许存放16KB/2-200行记录,即7992行记录
  • 这里提到的row-oriented的数据库,也就是说,存在有column-oriented的数据库
  • MySQL infobright存储引擎就是按列来进行存放数据的,这对于数据仓库下的分析类SQL语句的执行及数据压缩非常有帮助。类似的数据库还有Sybase IQ、Google BigTable

转载:https://blog.csdn.net/qq_41453285/article/details/104123716
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场