Innodb Buffer Pool内部结构
1. Innodb Buffer 功能
Innodb buffer pool的主要功能存储外存页面在内存中的镜像.镜像有如下2种镜像:
(1)只读镜像:只读镜像读取的是非脏页。
(2)更新镜像:更新镜像为buffer pool中的脏页。
Innodb实现了行级多版本(MVCC),而不是整个页的多版本。Oracle在实现中存在第三种镜像,就是版本镜像。而innodb中确实没有。在innodb中任何外存中的脏页的读取以及更新都是在buffer pool中进行。
2 .Innodb Pool数据结构
在innodb buffer pool整个模块设计的时候,使用了如下主要的数据结构:
(1)buf_page_t:外存页在内存中的存储结构,包括压缩页和非压缩页。
(2)buf_block_t:在innodb buffer pool存在页的页控制结构,该结构控制页包含的互斥量等内存结构保护的对象。每一个结构都包含buf_page_t的成员。内存中的每一个页都有一个对应的buf_block_t的结构。在innodb在指定innodb_buffer_pool_size启动时候,会预先按照该参数指定的值计算当前内存池可以存储多少个页以及每个页需要的buf_block_t的数量,来对该结构分配内存。MySQL 中每个连接线程想要访问内存池中的页必须获取对应页的buf_block_t的mutex。
(3)buf_chunk_t:该结构主要包含了buf_block_t的一个数组。
(4)buf_pool_t:innodb buffer pool的结构,该结构包含了压缩页和非压缩页的哈希表维护、buf_chunk_t的维护、以及3种链表。分别为free list、flush LRU list、LRU list。同时innodb buffer pool还维护一个哈希表,该哈希表存在于内存中,目前buffer pool中存在的页的哈希表。每个innod buffer pool都有一个mutex来保护该内存结构。
3. 页的映射
Innodb buffer pool中每一个页会按照内部规则,依据每个页的space_id以及offset进行函数运算把外存中的页加载到内存池中。每个连接线程访问某个页时候会依据页的space id以及offset进行函数运算,该函数返回去某个buffer pool 的指针,最后在指定的(函数运算后得到的某个buffer pool) buffer pool中进行查找,如果找到了就直接访问;如果没有找到就依据页的space id以及offset进行函数运算出需要加载到哪个buffer pool中,最后通过IO操作把页加载到指定的buffer pool中。例如目前innodb buffer pool分配了20G内存,而innodb buffer pool的数量是8个,如果访问一个页不再内存池中,则从外存中加载页到这个8个内存池中的一个。如果存在于内存池中,则只需要查找一个内存池中的哈希表是否有该页的存在。这样能提高搜索的效率。
以上提到依据space id以及offsset获取某个buffer pool实例的函数是buf_pool_get,返回值为buf_pool_t。如下是该函数的原型:
/******************************************************************//**
Returns the buffer pool instance given space and offset of page
@return buffer pool */
UNIV_INLINE
buf_pool_t*
buf_pool_get(
/*==========*/
ulint space, /*!< in: space id */
ulint offset) /*!< in: offset of the page within space */
4. 内存池的转储/恢复
MySQL在新版本以后新增buffer pool的转储以及恢复。它的主要应用场景在MySQL服务器需要重启,但是又不影响之前高峰期的时候的热点数据。
Innodb buffer pool的转储是把内存中的每个页的space id以及offset的组合转储到外存中,而不是把整个内存中所有的内容都转储出来。Buffer pool的转储需要经历如下步骤:
- 获取buffer pool的mutex。
- 遍历pool中的LRU list,读取其中page的space id以及offset,存入到内存数组中。
- 释放buffer pool中的mutex。
- 将数组中的内容写入到磁盘文件。