ITPub博客

首页 > 数据库 > MySQL > MySQL内存管理

MySQL内存管理

原创 MySQL 作者:caohongfeng666 时间:2021-01-03 18:23:54 1 删除 编辑

标签:MySQL

标签:MySQL结构

标签:MySQL内存管理


1. Buffer Pool主要的基础结构buf_pool_t、buf_block_t和buf_page_t。

(1). 

Buffer Pool实例,大小等于innodb_buffer_pool_size/innodb_buffer_pool_instances,

每个Buffer Pool Instance都有自己的锁,信号量,物理块(Bufferchunks)以及逻辑链表(List)。

即各个instance之间没有竞争关系,可以并发读取与写入。所有instance的物理块(Buffer chunks)在数据库启动的时候被分配,直到数据库关闭内存才予以释放。

每个Buffer Pool Instance有一个page hash链表,通过它,使用space_id和page_no就能快速找到已经被读入内存的数据页,而不用线性遍历LRU List去查找。

page hash是为了避免扫描LRU List。


struct buf_pool_t { //保存Buffer Pool Instance级别的信息

    ...

    ulint instance_no; //当前buf_pool所属instance的编号

    ulint curr_pool_size; //当前buf_pool大小

    buf_chunk_t *chunks; //当前buf_pool中包含的chunks

    hash_table_t *page_hash; //快速检索已经缓存的Page

    UT_LIST_BASE_NODE_T(buf_page_t) free; //空闲Page链表

    UT_LIST_BASE_NODE_T(buf_page_t) LRU; //Page缓存链表,LRU策略淘汰

    UT_LIST_BASE_NODE_T(buf_page_t) flush_list; //还未Flush磁盘的脏页保存链表

    BufListMutex XXX_mutex; //各个链表的互斥Mutex

    ...

}


(2).

Buffer chunks是每个Buffer Pool Instance中实际的物理存储块数组,一个Buffer Pool Instance中有一个或多个chunk,每个chunk的大小默认为128MB,最小为1MB,且这个值在8.0中时可以动态调整生效的。

Buffer Chunks是最低层的物理块,在启动阶段从操作系统申请,直到数据库关闭才释放。


每个Buffer chunk中包含一个buf_block_t的blocks数组(即Page)。

blocks数组中的每个元素buf_block_t是一个数据页结构体,其中包含了一个指向具体数据页的*frame指针,以及具体的控制体buf_page_t。



struct buf_block_t { //Page控制体

    buf_page_t page; //这个字段必须要放到第一个位置,这样才能使得buf_block_t和buf_page_t的指针进行转换

    byte *frame; //指向真正存储数据的Page

    BPageMutex mutex; //block级别的mutex

    ...

}


(3).

class buf_page_t {

...

    page_id_t id; //page id

    page_size_t size; //page 大小

    ib_uint32_t buf_fix_count; //用于并发控制

    buf_io_fix io_fix; //用于并发控制

    buf_page_state state; //当前Page所处的状态,后续会详细介绍

    lsn_t newest_modification; //当前Page最新修改lsn

    lsn_t oldest_modification; //当前Page最老修改lsn,即第一条修改lsn

    ...

}


(4).buf_page_state,这是一个枚举类型,标识了每个Page所处的状态,在读取和访问时都会对应不同的状态转换。

enum buf_page_state {

  BUF_BLOCK_POOL_WATCH, //注释是给Purge使用的

  BUF_BLOCK_ZIP_PAGE, //压缩Page状态

  BUF_BLOCK_ZIP_DIRTY, //压缩页脏页状态

  BUF_BLOCK_NOT_USED, //保存在Free List中的Page

  BUF_BLOCK_READY_FOR_USE, //当调用到buf_LRU_get_free_block获取空闲Page,此时被分配的Page就处于这个状态

  BUF_BLOCK_FILE_PAGE, //正常被使用的状态,LRU List中的Page都是这个状态

  BUF_BLOCK_MEMORY, //用于存储非用户数据,比如系统数据的Page处于这个状态

  BUF_BLOCK_REMOVE_HASH //在Page从LRU List和Flush List中被回收并加入Free List时,需要先从Page_hash中移除,此时Page处于这个状态

};



2. 页链表

链表节点是数据页的控制体(控制体中有指针指向真正的数据页),链表中的所有节点都有同一的属性,引入其的目的是方便管理。

以下所有的链表中的每个节点都是数据页控制体(buf_page_t)。


2.1

Free List:如其名,Free List中存放的都是未曾使用的空闲Page,InnoDB需要Page时从Free List中获取,如果Free List为空,即没有任何空闲Page,则会从LRU List和Flush List中通过淘汰旧Page和Flush脏Page来回收Page。在InnoDB初始化时,会将Buffer chunks中的所有Page加入到Free List中。


2.2

LRU List:所有从数据文件中新读取进来的Page都会缓存在LRU List,并通过LRU策略对这些Page进行管理。LRU List实际划分为Young和Old两个部分,其中Young区保存的是较热的数据,Old区保存的是刚从数据文件中读取出来的数据,如果LRU List的长度小于512,则不会将其拆分为Young和Old区。当InnoDB读取Page时,首先会从当前Buffer Pool Instance的page_hash查找,并分为三种情况来处理:


如果在page_hash找到,即Page在LRU List中,则会判断Page是在Old区还是Young区,如果是在Old区,在读取完Page后会把它添加到Young区的链表头部.

如果在page_hash找到,并且Page在Young区,需要判断Page所在Young区的位置,只有Page处于Young区总长度大约1/4的位置之后,才会将其添加到Young区的链表头部。

如果未能在page_hash找到,则需要去数据文件中读取Page,并将其添加到Old区的头部。

LRU List采用非常精细的LRU淘汰策略来管理Page,并且用以上机制避免了频繁对LRU 链表的调整。


2.3

Flush List:所有被修改过且还没来得及被flush到磁盘上的Page(脏页),都会被保存在这个链表中。所有保存在Flush List上的数据都会在LRU List中,但在LRU List中的数据不一定都在Flush List中。在Flush List上的每个Page都会保存其最早修改的lsn,即oldest_modification,虽然一个Page可能被修改多次,但只记录最早的修改。Flush List上的Page会按照其各自的oldest_modification进行降序排序,链表尾部保存oldest_modification最小的Page,在需要从Flush List中回收Page时,从尾部开始回收。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/25380026/viewspace-2747123/ ,如需转载,请注明出处,否则将追究法律责任。

下一篇: MySQL data page
请登录后发表评论 登录
全部评论
曹老道

注册时间:2013-07-31

  • 博文量
    23
  • 访问量
    23201