MySQL Page读取和淘汰过程分析

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/duxingxia356/article/details/101195054

Page磁盘读取和加载的互斥

buf_page_get_gen
|    | ==> rw_lock_s_lock(hash_lock);
|    | ==> block = (buf_block_t*) buf_page_hash_get_low(buf_pool, space, offset, fold);
|    | ==> if (block == NULL) rw_lock_s_unlock(hash_lock); 
|        | ==> buf_read_page(space, zip_size, offset)  buf_read_page_low
|            | ==> bpage = buf_page_init_for_read(err, mode, space, zip_size, unzip, tablespace_version, offset);
|                | ==> block = NULL;
|                | ==> buf_pool_mutex_enter(buf_pool);
|                | ==> rw_lock_x_lock(hash_lock);
|                | ==> watch_page = buf_page_hash_get_low(buf_pool, space, offset, fold);
|                | ==> if (watch_page && !buf_pool_watch_is_sentinel(buf_pool, watch_page))
|                    | ==> rw_lock_x_unlock(hash_lock);
|                | ==> rw_lock_x_unlock(hash_lock);
|                | ==> data = buf_buddy_alloc(buf_pool, zip_size, &lru);
|                    | ==> buf_buddy_alloc_low(buf_pool, buf_buddy_get_slot(size), lru));
|                        | ==> block = buf_LRU_get_free_only(buf_pool);
|                | ==> rw_lock_x_lock(hash_lock);
|                | ==> /* If buf_buddy_alloc() allocated storage from the LRU list, it released and reacquired buf_pool->mutex.  Thus, we must check the page_hash again, as it may have been modified. */
|                | ==> if (UNIV_UNLIKELY(lru)) {
|                    | ==> watch_page = buf_page_hash_get_low(buf_pool, space, offset, fold);
|                | ==> if (watch_page && !buf_pool_watch_is_sentinel(buf_pool, watch_page)) rw_lock_x_unlock(hash_lock); watch_page = NULL; buf_buddy_free(buf_pool, data, zip_size);
|                | ==> HASH_INSERT(buf_page_t, hash, buf_pool->page_hash, fold, bpage);
|                | ==> rw_lock_x_unlock(hash_lock);
|                | ==> buf_LRU_add_block(bpage, TRUE/* to old blocks */);
|                | ==> buf_pool_mutex_exit(buf_pool);
|    | ==> buf_block_fix(fix_block);
|        | ==> #ifdef PAGE_ATOMIC_REF_COUNT
|        | ==> 	os_atomic_increment_uint32(&block->page.buf_fix_count, 1);
|        | ==> #else
|        | ==> 	ib_mutex_t*	block_mutex = buf_page_get_mutex(&block->page);
|        | ==> 	mutex_enter(block_mutex);
|        | ==> 	++block->page.buf_fix_count;
|        | ==> 	mutex_exit(block_mutex);
|    | ==> rw_lock_s_unlock(hash_lock);
|    | ==> buf_wait_for_read(fix_block);

它的过程是:

  1. 对Page_Hash加s锁,判断是否存在。
  2. 如果不存在,释放s锁,加bp的互斥锁、加x锁。再次判断是否存在。
  3. 如果不存在,释放x锁,申请空闲Block。
  4. 然后加x锁,判断是否存在,如果不存在,插入空闲BLock。释放x锁
  5. 把block加入lru链表。释放bp的互斥锁。

步骤3释放x锁的原因是,线程持有bp的互斥锁,对lru\freelist的操作都依靠互斥锁。因此可以通过bp的互斥锁,阻塞其他线程申请空闲Block。但是在从Lru申请BLock过程中,可能出现由于休眠等待需要释放互斥锁,而导致其他线程进入,因此第4步根据空闲BLock的来源判断是否需要重新Check。

可以看到,从磁盘读取过程中,对hash的互斥成本是比较高的,加了2次x锁。这个地方后续版本中有优化。

Page内存读取和Page淘汰的互斥

buf_flush_batch
|    | ==> //加LRU LOCK
|    | ==> buf_pool_mutex_enter(buf_pool);
|    | ==> count = buf_do_LRU_batch(buf_pool, min_n);
|        | ==> buf_flush_LRU_list_batch(buf_pool, max - count);
|            | ==> bpage = UT_LIST_GET_LAST(buf_pool->LRU);
|            | ==> mutex_enter(block_mutex);
|            | ==> evict = buf_flush_ready_for_replace(bpage);
|                | ==> bpage->buf_fix_count == 0
|            | ==> mutex_exit(block_mutex);
|            | ==> if (evict) buf_LRU_free_page
|                | ==> rw_lock_x_lock(hash_lock);
|                | ==> mutex_enter(block_mutex);
|                | ==> //重新判断引用计数
|                | ==> buf_page_can_relocate
|                | ==> //从LRU和HASH表删除
|                | ==> buf_LRU_block_remove_hashed
|                    | ==> buf_LRU_remove_block(bpage);
|                    | ==> HASH_DELETE(buf_page_t, hash, buf_pool->page_hash, fold, bpage);
|                    | ==> rw_lock_x_unlock(hash_lock);
|                | ==> buf_pool_mutex_exit(buf_pool);
|                | ==> buf_pool_mutex_enter(buf_pool);
|                | ==> buf_LRU_block_free_hashed_page((buf_block_t*) bpage);
|                    | ==> buf_LRU_block_free_non_file_page(block);
|                        | ==> UT_LIST_ADD_FIRST(list, buf_pool->free, (&block->page));
|    | ==> buf_pool_mutex_exit(buf_pool);

Page淘汰过程如下:

  • 加BP的互斥锁
  • 从LRU链表的获取最老的数据页
  • 确认Page是否可以被淘汰
  • 如果可以淘汰,加hash_page的x锁,此时重新判断,因为前述判断无法拦截对Page的访问,因此此时对Page_hash加x锁,进行准确的判断。
  • 如果确实可以淘汰,从Page_Hash中删除,释放hash的x锁。
  • 将Page加入空闲链表

猜你喜欢

转载自blog.csdn.net/duxingxia356/article/details/101195054