A.持有buf_pool->LRU_list_mutex锁B.首先,尝试从buf_pool->unzip_LRU上释放block,这种情况下不会释放压缩页数据freed = buf_LRU_free_from_unzip_LRU_list(buf_pool, n_iterations, have_LRU_mutex);
>>判断是否从unzip_LRU上驱逐block
理论上讲,从buf_pool->unzip_LRU上应该更容易获得一个block,因为我们可以选择一个脏块(只驱逐解压页),但当我们尝试5次还是没有找到时,则直接返回到正常的驱逐block的逻辑,即从LRU上获取
另外也有函数buf_LRU_evict_from_unzip_LRU用来判断是否从unzip_LRU上驱逐block,其判断逻辑如下:
>>>需要持有buf_pool->LRU_list_mutex
>>>如果buf_pool->unzip_LRU长度为0 ,返回FALSE
>>>如果buf_pool->unzip_LRU小于buf_pool->LRU的十分之一,返回FALSE
>>>如果buf_pool->freed_page_clock == 0,表示之前没有进行过任何block驱逐,默认假设工作负载为disk bound,返回TRUE
freed_page_clock是一个序列号,用来计数从LRU尾部移除的block数,可以不加锁读取该变量
>>>计算最近的平均IO量
io_avg = buf_LRU_stat_sum.io / BUF_LRU_STAT_N_INTERVAL
+ buf_LRU_stat_cur.io;
最近50秒的平均IO+当前的IO,获得最近的平均IO负载
unzip_avg = buf_LRU_stat_sum.unzip / BUF_LRU_STAT_N_INTERVAL
+ buf_LRU_stat_cur.unzip;
最近50秒平均解压page的次数+当前解压page的次数,获得最近每秒平均解压page次数
我们对io_avg进行加权(BUF_LRU_IO_TO_UNZIP_FACTOR),依次判断是IO-BOUND还是CPU-BOUND
当unzip_avg <= io_avg * BUF_LRU_IO_TO_UNZIP_FACTOR时,表示这是IO-Bound的,返回TRUE
当unzip_avg > io_avg * BUF_LRU_IO_TO_UNZIP_FACTOR时,表示这是CPU-Bound的,返回FALSE。
BUF_LRU_IO_TO_UNZIP_FACTOR是一个宏,值为50。这是一个hard code值,但正如bug#64181所提到的,快速存储例如SSD可能并不适合这样的加权,因为SSD相对传统硬盘具有更快的IO速度。
当从buf_LRU_evict_from_unzip_LRU返回值为false时,则从LRU扫描,如果为true,则尝试从unzip_lru扫描
>>计算在unzip中的最大扫描距离
distance = 100 + (n_iterations
* UT_LIST_GET_LEN(buf_pool->unzip_LRU)) / 5
其中n_iterations<5
>>从buf_pool->unzip_LRU尾部开始扫描,满足如下条件可以进行驱逐
buf_block_get_state(block) == BUF_BLOCK_FILE_PAGE;
block->in_unzip_LRU_list;
block->page.in_LRU_list;
然后从unzip_lru上释放block:
freed = buf_LRU_free_block(&block->page, FALSE, have_LRU_mutex);
>>>检查block是否被pin住(buffer-fixed or I/O-fixed),是的话直接返回false
>>>如果表正在被删除(bpage->space_was_being_deleted) 并且bpage->oldest_modification !=0时调用buf_flush_remove(bpage)从flush list上删除该block
其中bpage->oldest_modification记录了修改该block,但尚未刷入磁盘的日志记录起始LSN。值为0表示所有的修改都刷入了磁盘。
>>>分配一个page描述符(buf_page_t)
b = buf_page_alloc_descriptor();
然后将bpage拷贝到b中
memcpy(b, bpage, sizeof *b);
这里bpage->zip.data的指针也被拷贝到b,因此可以通过b访问压缩页,而在随后将bpage->zip.data置为NULL
>>>从LRU和Page hash上移除page
buf_LRU_block_remove_hashed_page(bpage, zip)
当前流程的zip为false,表示不释放压缩page
|–>buf_LRU_remove_block(bpage);
|–>从buf_pool->page_hash中移除
这里zip参数为false,因此无需释放压缩页数据。但如果从LRU释放,这里可能会成为瓶颈(buf_buddy_free做碎片整理)
>>>将b插入到buf_pool->page_hash和buf_pool->LRU中。
>>>移除可能存在的adaptive hash index记录
btr_search_drop_page_hash_index((buf_block_t*) bpage, NULL);
>>>计算b->zip.data的checksum,并写入压缩页。
>>>将空出来的bpage加入到buf_pool->free上
buf_LRU_block_free_hashed_page((buf_block_t*) bpage, FALSE);
C.如果buf_pool->unzip_LRU上找不到空闲块,这时候会去从buf_pool->LRU上获取,这种情况下,如果驱逐的是压缩表的block,还会释放压缩页if (!freed) {freed = buf_LRU_free_from_common_LRU_list(buf_pool, n_iterations, have_LRU_mutex);}该函数会从buf_pool->LRU的尾部开始扫描,在没有压缩表的情况下这也是普遍调用的函数函数freed = buf_LRU_free_block(bpage, TRUE, have_LRU_mutex)会被调用到去从LRU上释放该块,注意这里第二个参数为TRUE,这表明会同时释放压缩页,并做碎片整理工作。buf_LRU_free_block->buf_LRU_block_remove_hashed_page->buf_buddy_free->buf_buddy_free_low,另外buf_LRU_block_remove_hashed_page中还会释放bpage的page描述符(buf_page_free_descriptor)
related bug:http://bugs.mysql.com/bug.php?id=64344
如bug#64344所提到的,malloc/free是在持有buffer pool锁的情况下进行的,这会对并发操作产生开销。D.更新buf_pool->LRU_flush_ended计数并释放buf_pool->LRU_list_mutexif (!freed) {buf_pool->LRU_flush_ended = 0;} else if (buf_pool->LRU_flush_ended > 0) {buf_pool->LRU_flush_ended–;}