本文尝试对InnoDB表空间管理进行一个大致的分析,为了避免混淆,对比较关键的一些名词使用代码中的原文表示。另外本文假定读者已经基本了解MySQL/InnoDB相关的一些基础知识和概念。
上图是InnoDB很经典的一张关于表空间组织结构图,所谓表空间实际是InnoDB对其需要持久化的数据文件进行的一种物理组织格式,代码主要在fsp/fsp0fsp.c文件中,主要涵盖了所有关于segment,extent,page相关物理结构的组织和管理。
一. page基本概念
page 是innodb管理其物理文件的一个基本单位,默认大小为16K,在include/univ.i文件中定义:
- /* The 2-logarithm of UNIV_PAGE_SIZE: */
-
#define UNIV_PAGE_SIZE_SHIFT 14
-
/* The universal page size of the database */
- #define UNIV_PAGE_SIZE (1 << UNIV_PAGE_SIZE_SHIFT)
page 被innoDB用来存放BTree节点,数据,segment inode等,其完整的类型定义在 include/fil0fil.h中定义:
- /** File page types (values of FIL_PAGE_TYPE) @{ */
-
#define FIL_PAGE_INDEX 17855 /*!< B-tree node */
- #define FIL_PAGE_UNDO_LOG 2 /*!< Undo log page */
而象page type这样标识一个page类型的元数据信息都是存放在fil header中,fil header的定义也在include/fil0fil.h中:主要字段包括:
- #define FIL_PAGE_OFFSET 4 //page在表空间内的page no(页号)
-
#define FIL_PAGE_PREV 8 //前一个页的page no
-
#define FIL_PAGE_NEXT 12 //下一个页的page no
-
#define FIL_PAGE_TYPE 24 //page类型
- #define FIL_PAGE_DATA 38 //fil header结束位置
这里需要说明的是:
page no页号是相对于整个表空间的,而一个表空间可以包含有多个文件,而这个页号则是一直连续下去。
每个page都存放了其前后页的page no。
fil header结束位置是38 即整个fil header占用38个字节,这38字节是所有类型page都必须要有的,根据page类型不同后续会说明在这38字节之后还有其它的元数据信息需要存储。
当然关于page我们这里只简单描述了fil header,实际还有很多内容我们后续在依次讨论
二. Extent基本概念。
page实际是一个比较小的物理组织单位,那么我们需要一个更大一点的单位来组织page,这个单位就是extent(称为簇或者区),一个extent管理64个page,这个定义在include/fsp0types.h文件中定义:
- /** File space extent size (one megabyte) in pages */
- #define FSP_EXTENT_SIZE (1 << (20 – UNIV_PAGE_SIZE_SHIFT))
那么extent又是一个什么样的组织结构,它如何被存储的呢?实际上所谓的extent实际是通过一个叫extent descriptor(簇描述符)来表示的,具体定义在fsp/fsp0fsp.c中:
- /* EXTENT DESCRIPTOR
-
=================
-
File extent descriptor data structure: contains bits to tell which pages in
-
the extent are free and which contain old tuple version to clean. */
-
/*-------------------------------------*/
- #define XDES_ID 0 /* The identifier of the segment to which this extent belongs */
- #define XDES_FLST_NODE 8 /* The list node data structure for the descriptors */
- #define XDES_STATE (FLST_NODE_SIZE + 8) /* contains state information of the extent */
- #define XDES_BITMAP (FLST_NODE_SIZE + 12) /* Descriptor bitmap of the pages in the extent */
- /*-------------------------------------*/
实际上每64个page就会有对应的一个extent descriptor存在,主要目的是标识这64个page的状态(空闲,半空闲,还是保留给某个segment使用),以及能够快速从这64个page中找到空闲的page。
这里分别解释下:
XDES_ID 存放该extent所属segment的id
XDES_FLST_NODE实际是一个文件链表节点,这样可以把extent descriptor组织成一个链表,实际上我们在extent更上层管理extent时会把extent链在一起组成链表,那么实际链表的节点就是extent descriptor的这个字段,比如某个segment的extent free list和表空间的extent frag list都是通过这个节点组织extent链表的,这个后续详细说明
XDES_STATE标识该extent descriptor所代表的extent的所属状态,定义如下:
- /* States of a descriptor */
-
#define XDES_FREE 1 /* extent is in free list of space */
-
#define XDES_FREE_FRAG 2 /* extent is in free fragment list of space */
-
#define XDES_FULL_FRAG 3 /* extent is in full fragment list of space */
- #define XDES_FSEG 4 /* extent belongs to a segment */
4. 最后的一个字段bitmap用来判断这个extent中哪些page是空闲的。
这样算下来一个extent descriptor会占用40字节
- /* File extent data structure size in bytes. */
-
#define XDES_SIZE \
- (XDES_BITMAP + UT_BITS_IN_BYTES(FSP_EXTENT_SIZE * XDES_BITS_PER_PAGE))
XDES_SIZE = 24 byte + (64 * 2) bit = 40 byte
三. Extent descriptor page 基本概念
分析过extent是通过extent descriptor来表示的,那么我们自然关心这个descriptor如何存储。
实际上extent descriptor也是存储在一个page中的,那么一个page有16K,而一个descriptor只有40字节,所以一个page肯定可以存放很多个extent descriptor,那么我们就管这个专门用来存放extent descriptor的page 称为extent descriptor page。
代码中使用了UNIV_PAGE_SIZE这个定义,作为一个extent descriptor page能够管理的page数量,也就是一个extent descriptor page 可以存放 16K / 64 = 256 个extent descriptor
熟悉了extent descriptor page的基本概念后,我们具体看下这个page的结构:
实际上extent descriptor在该页上存储的位置从该页的第150字节开始依次存储,定义在fsp/fsp0fsp.c中如下:
- /* Offset of the descriptor array on a descriptor page */
- #define XDES_ARR_OFFSET (FSP_HEADER_OFFSET + FSP_HEADER_SIZE)
这里涉及一个FSP_HEADER我们后续再说,也就是需要记住对于extent descriptor page来说,其extent descriptor 位置是在 fil header + fsp header之后存储的,如图:
四. Extent descriptor page 的布局。
最后一个我们需要了解的是关于extent descriptor page本身是如何在表空间文件内布局的。
因为一个descriptor page可以描述16K个页,所以innodb在每16K个页间插入一个descriptor page,这样整个extent就完整的组织起来了,也就是说一个表空间的page 0 ,page 16384 , page 2 * 16384 ...都是extent descriptor page 。