MySQL内存管理

来源:这里教程网 时间:2026-03-01 15:33:13 作者:

标签: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时,从尾部开始回收。

相关推荐