InnoDB buffer pool初始化
InnoDB的buffer pool结构
从MySQL 5.7.5开始,buffer pool以chunk为单位进行管理,多个chunk组成一个instance。通常一个mysql实例会包含1到多个buffer pool的instance。
- buffer pool的instance数量由innodb_buffer_pool_instances值指定;
- 每个instance的chunk数量由innodb_buffer_pool_chunk_size值间接确定;
一个具有两个instance的buffer pool,chunk数量为2的示意图如图1.1所示。
因此一个mysql实例的InnoDB buffer pool自顶向下分为buf_pool,instance,chunk,block四层逻辑。
Innodb buffer pool初始化
既然InnoDB的buffer pool自定向下分为了四层逻辑,初始化自然也是按照这个顺序进行。
buf_pool_init
设置一些全局变量,根据buf_pool_size总量和instance数量计算单个instance的size,多次调用buf_pool_init_instance创建指定数量的实例,每个instance对象的类型为buf_pool_t
;
buf_pool_init_instance
每个instance有专属的free-list,flush-list,LRU-list和其他list。根据instance的size和chunk size确定一个instance包含多少个chunk,多次调用buf_chunk_init创建指定数量的chunk,每个chunk对象的类型为buf_chunk_t
;
buf_chunk_init
到了chunk层,才会给单个chunk的实际的buffer页分配一整块连续的内存空间,这段空间通过buf_chunk_t::mem域引用,另外buf_chunk_t::blocks也引用同样的地址,但是后者类型为buf_block_t *,代表页控制块列表。也就是说,在每个chunk的一整块内存空间中,开头是控制块列表,紧紧跟着控制块列表的才是实际的buffer空间;
根据单个chunk中page页的数量,多次调用buf_block_init初始化所有的控制块,控制块的类型为buf_block_t
;
计算单个chunk需要的内存总量
chunk初始化中需要负责为实际的所有的控制块和buffer分配连续空间,因此这段内存空间的总量是需要专门计算的。因为输入参数innodb_buffer_pool_chunk_size
只是给出了buffer的size,每个chunk还额外包括所有的控制块占用的内存量;
- 将输入的size参数向下对齐到页大小
UNIV_PAGE_SIZE
的倍数得到mem_size
; mem_size
加上额外的一段同样对齐UNIV_PAGE_SIZE
并且可以容纳指定数量的控制块的内存大小;
找到页缓存实际的内存起始偏移
如上所述,这一整块内存一开始是连续的所有的控制块结构,然后跟着是实际的页空间。实际上,由于所有的控制块结构都放在按页大小对齐的一段内存空间中,且页空间也是从页对齐的偏移起始的,因此两者之间是有一段空隙的,而这里找到这个空隙(其实是确定实际的页数量和首页起始地址)的算法很精巧:
frame = (byte*) ut_align(chunk->mem, UNIV_PAGE_SIZE);
chunk->size = chunk->mem_pfx.m_size / UNIV_PAGE_SIZE
- (frame != chunk->mem);
/* Subtract the space needed for block descriptors. */
{
ulint size = chunk->size;
while (frame < (byte*) (chunk->blocks + size)) {
frame += UNIV_PAGE_SIZE;
size--;
}
chunk->size = size;
}
frame一开始指向内存空间头部chunk::mem
,frame每次向后移动一整页,代表该chunk的页总数减少一页,此时如果frame依然小于chunk::blocks + size
,说明当前frame指向的位置依然无法存放下所有的控制块。如果frame已经超过了chunk::blocks + size
,说明当前frame指向的位置可以存放下所有的控制块了,循环退出。
buf_block_init
单个控制块类型buf_block_t
的初始化就非常简单了,初始化其所有字段域即可。类型的开头是buf_page_t
类型的页描述符,描述该页当前的状态,然后frame
字段直接指向对应的页内存空间。