美文网首页mysql
InnoDB存储空间

InnoDB存储空间

作者: 骁兵 | 来源:发表于2019-02-28 15:21 被阅读14次
    关键词
    • 表空间、段、区、页、行
    • 一个段256M,一个区1M,一个页16K,一个段=256区=256*64个页
    • 表空间中每连续256个区需要一个XDES来维护,XDES里面有256个entry,每一个entry描述一个区的状态、区里面64个页的状态、上一个和下一个区的指针、区属于哪一个段。
    • 段分为数据段,索引段,回滚段等,在 MySQL中,数据是按照B+树来存储,因此数据即索引,因此数据段的页即为B+树的叶子节点,索引段的页为B+树的非叶子节点。
    • Page链接起来就是一个双向链表的结构
    • B+ 树在查找对应的记录时,并不会直接从树中找出对应的行记录,它只能获取记录所在的页,将整个页加载到内存中,再通过 Page Directory 中存储的稀疏索引和 n_owned、next_record 属性取出对应的记录

    https://blog.jcole.us/2013/01/04/page-management-in-innodb-space-files/
    https://blog.jcole.us/innodb/

    索引组织表

    层级划分


      InnoDB 实现的表空间是在文件系统之上又构建的一层逻辑存储的空间管理
      InnoDB 最小读写单位是 Page,page 采用默认的 16K
      为了高效管理,又将连续的 64 个页称为 “extent”,还在 extents 上层添加了一个 segment inode 管理,最多可以管理 256 个 extents
      一个表空间文件,实际就是一系列 Pages 的组合 (最大 232),为了方便管理,你可以理解为做了三级的划分,分别是 segment、extent、page 。
      段:ibd表空间文件会被分成多个段(segment),每个段和一个索引关联
      区:段以区(extent)为大小进行增长或缩小,一个区只能属于一个段,并且大小为1MB
      页:页(page)是区的子元素,默认大小16KB
      行:一个页可以包含2到N行数据,这取决与每个行的大小,但因为限制了最小存放2行数据,所以每一行的大小必须小于8000bytes
      1段 = 256区 = 256*64页
    查看页信息
    查看每个页的详细信息
    innodb_page_info -v dept_emp.ibd
    

      也可以使用py_innodb_page_info工具

    > python py_innodb_page_info.py/usr/local/mysql/data/ibdata1 
    Total number of page:83584
    Insert Buffer Free List:204 
    Freshly Allocated Page:5467 
    Undo Log Page:38675
    File Segment inode:4 
    B-tree Node:39233 
    File Space Header:1
    

      可以看到共有83 584个页,其中插入缓冲的空闲列表有204个页、5467个可用页、38 675个Undo页、39 233个数据页,等等。你可以通过添加-v参数来查看更详细的内容。

    表空间
      Space File 主要包括了两类:系统空间 (system space) 和 表空间 (Per-table space files)
      表空间需要打开 innodb_file_per_table 变量之后才会生效
      Page0 肯定是 “File SPace HeaDeR”,其中维护了 extent 相关的信息,例如那些是空闲的、满的、有碎片的 (fragmented)。在 FSP_HDR 页中,只能维护 256 个 extent 相关的信息 ( 256 extent==16,384 pages==256MiB),这也就导致每隔 256M 都需要有一个 XDES 来管理接下来的 extent 元信息。
    • File Segment ID:extent属于哪个段
    • List node for XDES list:指向上一个和下一个extent
    • State:区状态
    • Page State Bitmap:区里面每一个页的状态,一个页的状态用2个位表示,一个区1M,一个页16K,一个区=64个页,64 x 2 = 128 bits = 16bytes

      表空间是由不同的段组成的,常见的段有:数据段,索引段,回滚段等等。
      在 MySQL中,数据是按照B+树来存储,因此数据即索引,因此数据段的页即为B+树的叶子节点,索引段的页为B+树的非叶子节点。
      回滚段用于存储undo日志,用于事务失败后数据回滚以及在事务未提交之前通过undo日志获取之前版本的数据,在InnoDB1.1版本之前一个InnoDB,只支持一个回滚段,支持1023个并发修改事务同时进行,在InnoDB1.2版本,将回滚段数量提高到了128个,也就是说可以同时进行128*1023个并发修改事务。
      每个段在开始时,会根据数据量的需要,逐步增加页的数量,当页数量超过32页时,会以64个页为单位,增大页数量。

      使用py_innodb_page_info工具时,page level等于0000的,表示叶子节点,B-tree Node是叶子节点和非叶子节点的总数。叶子节点属于数据段。

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

    1. 数据页(B-tree Node)
    2. undo页(undo Log Page)
    3. 系统页 (System Page)
    4. 事物数据页 (Transaction System Page)
    5. 插入缓冲位图页(Insert Buffer Bitmap)
    6. 插入缓冲空闲列表页(Insert Buffer Free List)
    7. 未压缩的二进制大对象页(Uncompressed BLOB Page)
    8. 压缩的二进制大对象页 (compressed BLOB Page)
      我们重点关注和数据组织结构相关的字段:Page的头部保存了两个指针,分别指向前一个Page和后一个Page,头部还有Page的类型信息和用来唯一标识Page的编号。根据这两个指针我们很容易想象出Page链接起来就是一个双向链表的结构
    数据页

      页是 InnoDB 存储引擎管理数据的最小磁盘单位,而 B-Tree 节点就是实际存放表中数据的页面。

      每一个页中包含了两对 header/trailer:内部的 Page Header/Page Directory 关心的是页的状态信息,而 Fil Header/Fil Trailer 关心的是记录页的头信息。
      在页的头部和尾部之间就是用户记录和空闲空间了,每一个数据页中都包含 Infimum 和 Supremum 这两个虚拟的记录(可以理解为占位符),Infimum 记录是比该页中任何主键值都要小的值,Supremum 是该页中的最大值:   User Records 就是整个页面中真正用于存放行记录的部分,而 Free Space 就是空余空间了,它是一个链表的数据结构,为了保证插入和删除的效率,整个页面并不会按照主键顺序对所有记录进行排序,它会自动从左侧向右寻找空白节点进行插入,行记录在物理存储上并不是按照顺序的,它们之间的顺序是由 next_record 这一指针控制的。
      B+ 树在查找对应的记录时,并不会直接从树中找出对应的行记录,它只能获取记录所在的页,将整个页加载到内存中,再通过 Page Directory 中存储的稀疏索引和 n_owned、next_record 属性取出对应的记录,不过因为这一操作是在内存中进行的,所以通常会忽略这部分查找的耗时。   page分为叶子页(leaf page)和非叶子节页(non-leaf page),叶子页包含了实际的行数据,非叶子页只包含了非叶子页指针或叶子页指针。B+树是平衡的,所以所有的分支都有一样的深度。
      InnoDB为每一个page分配了一个level级别:叶子页的level是0,随着树分支往上走,level不断递增。
      所有的非root页和非叶子页的页面,都被称为internal page。
      无论是叶子页还是非叶子页, 里面包含的行(record)都有指向下一行的指针(在同一个page里的偏移量),record链表从infimum开始,按键升序链接所有记录,终止于supremum。需要注意的时,在page里面的record数据,从物理角度来讲,在page里面并不是顺序存储,行在插入到page时,哪里有空缺空间就会插入到哪里,所以它们需要依赖的顺序只有链表上的顺序。   同一个level的page,每一个page会有一个指针(FIL头部)指向上一个和下一个page

      InnoDB存储引擎提供了Compact和Redundant两种格式来存放行记录数据。
      SHOW TABLE STATUS LIKE “table_name”,命令可以查看行格式
    Compact

    • 变长字段长度列表:此字段标识列字段的长度,与列字段顺序相反存放,若列长度小于255字节,用一个字节表示,若大于255字节,用两个字节表示,这也是 MySQL的VARCHAR类型最大长度限制为65535
    • NULL标志位:标识改列是否有空字段,有用1表示,否则为0,该标志位长度为ceil(N/8)(此处是 MySQL技术内幕-InnoDB存储引擎与官方文档有出入的地方);
    • 记录头信息:固定用5字节表示,具体含义如下:


    • 列数据:此行存储着列字段数据,Null是不占存储空间的;
    • 隐藏列:事务id和回滚列id,分别占用6、7字节,若此表没有主键,还会增加6字节的rowid列。
      Redundant
    • 字段长度偏移列表:存储字段偏移量,与列字段顺序相反存放,若列长度小于255字节,用一个字节表示,若大于255字节,用两个字节表示
    • 记录头信息:固定用6字节表示,具体含义如下:


    • 隐藏列:事务id和回滚列id,分别占用6、7字节,若此表没有主键,还会增加6字节的rowid列。
      行溢出
        关于行溢出,即Redundant格式、Compact格式存储很长的字符串,在该字段会存储该字符串的前768个字节的前缀(字段超过768字节则为变长字段),并将整个字符串存储在uncompress blob页中。

    索引

      InnoDB 存储引擎在绝大多数情况下使用 B+ 树建立索引,B+ 树是平衡树,它查找任意节点所耗费的时间都是完全相同的,比较的次数就是 B+ 树的高度。当数据必须从磁盘读取时,B+树可以保证最多的读取次数(取决于树的高度)。
    聚集索引和辅助索引

      数据库中的 B+ 树索引可以分为聚集索引(clustered index)和辅助索引(secondary index),它们之间的最大区别就是,聚集索引中存放着一条行记录的全部信息,而辅助索引中只包含索引列和一个用于查找对应行记录的『书签』。
    聚集索引
      聚集索引就是按照表中主键的顺序构建一颗 B+ 树,并在叶节点中存放表中的行记录数据。

      所有正常的表应该有且仅有一个聚集索引(绝大多数情况下都是主键),表中的所有行记录数据都是按照聚集索引的顺序存放的。
      当我们使用聚集索引对表中的数据进行检索时,可以直接获得聚集索引所对应的整条行记录数据所在的页,不需要进行第二次操作。

    辅助索引
      数据库将所有的非聚集索引都划分为辅助索引,但是这个概念对我们理解辅助索引并没有什么帮助;辅助索引也是通过 B+ 树实现的,但是它的叶节点并不包含行记录的全部数据,仅包含索引中的所有键和一个用于查找对应行记录的『书签』,在 InnoDB 中这个书签就是当前记录的主键。

      如果在表 users 中存在一个辅助索引 (first_name, age),那么它构成的 B+ 树大致就是上图这样,按照 (first_name, age) 的字母顺序对表中的数据进行排序,当查找到主键时,再通过聚集索引获取到整条行记录。

    相关文章

      网友评论

        本文标题:InnoDB存储空间

        本文链接:https://www.haomeiwen.com/subject/aunhuqtx.html