一个Page从磁盘读入buffer pool的过程
以下是边看代码边记录的,从磁盘读取一个压缩Page到buffer pool的的全过程,以函数buf_page_get_gen作为入口(基于MySQL5.7.31)
|-buf_page_get_gen
(如果请求的数据页已经在Buffer Pool中了,修改相应信息后,就直接返回对应数据页指针,如果Buffer Pool中没有相关数据页,则从磁盘中读取。)
|-buf_pool_t* buf_pool = buf_pool_get(page_id);
|- ulint ignored_page_no = page_id.page_no() >> 6;
page_id_t id(page_id.space(), ignored_page_no);
ulint i = id.fold() % srv_buf_pool_instances;
(依据space_id和page_no查找指定的数据页在哪个Buffer Pool Instance里面。这里有个小细节,page_no的第六位被砍掉,这是为了保证一个extent的数据能被缓存到同一个Buffer Pool Instance中,便于后面的预读操作。)
|-block = (buf_block_t*) buf_page_hash_get_low(buf_pool, page_id);
(在page hash中查找这个数据页是否已经被加载到对应的Buffer Pool Instance中)
|-if (block == NULL)
if (mode == BUF_GET_IF_IN_POOL_OR_WATCH)
(如果没有找到这个数据页且mode为BUF_GET_IF_IN_POOL_OR_WATCH则设置watch数据页(buf_pool_watch_set))
if (mode == BUF_GET_IF_IN_POOL|| mode == BUF_PEEK_IF_IN_POOL
|| mode == BUF_GET_IF_IN_POOL_OR_WATCH)
(如果没有找到数据页且mode为BUF_GET_IF_IN_POOL、BUF_PEEK_IF_IN_POOL或者BUF_GET_IF_IN_POOL_OR_WATCH函数直接返回空,表示没有找到数据页。)
|-buf_read_page(page_id, page_size)
(如果没有找到数据但是mode为其他,就从磁盘中同步读取(buf_read_page)。)
|-count = buf_read_page_low(&err, true,
0, BUF_READ_ANY_PAGE, page_id, page_size, false);
|-bpage = buf_page_init_for_read(err, mode, page_id, page_size, unzip);
|-block = buf_LRU_get_free_block(buf_pool);
(在读取磁盘数据之前,我们如果发现需要读取的是非压缩页,则先从Free List中获取空闲的数据页,如果Free List中已经没有了,则需要通过刷脏来释放数据页)
|-buf_LRU_check_size_of_non_data_objects(buf_pool);
(统计Free List和LRU List的长度,如果发现他们再Buffer Chunks占用太少的空间,则表示太多的空间被行锁,自使用哈希等内部结构给占用了,一般这些都是大事务导致的。这时候会给出报警。)
|-block = buf_LRU_get_free_only(buf_pool);
(查看Free List中是否还有空闲的数据页,如果有则直接返回,否则进入下一步。大多数情况下,这一步都能找到空闲的数据页。)
|-freed=buf_LRU_scan_and_free_block(buf_pool,n_iterations > 0);
(如果Free List中已经没有空闲的数据页了,则会尝试驱逐LRU List末尾的数据页)