调用栈
buf_LRU_get_free_block
----buf_LRU_scan_and_free_block/* 先看看能不能找到可以replaced */
----buf_flush_single_page_from_LRU/* 不行再刷新 */
--------buf_flush_page(buf_pool, bpage, BUF_FLUSH_SINGLE_PAGE, true)
------------buf_flush_write_block_low(bpage, flush_type, sync);
------------if (flush_type == BUF_FLUSH_SINGLE_PAGE) {
buf_dblwr_write_single_page(bpage, sync);
}
if (sync) {
ut_ad(flush_type == BUF_FLUSH_SINGLE_PAGE);
fil_flush(bpage->id.space());
/* true means we want to evict this page from the
LRU list as well. */
buf_page_io_complete(bpage, true);
}
----------------buf_page_io_complete
--------------------buf_flush_write_complete
------------------------buf_flush_remove
----------------------------UT_LIST_REMOVE(buf_pool->flush_list, bpage);
----------------buf_LRU_free_page(bpage, true); //从lru free page
LRU中的flush会从flush list中remove掉,相反flush list的flush不会从lru中remove,因为可能是读热点。
/* If no block was in the free list, search from the end of the LRU list and try to free a block there. If we are doing for the first time we'll scan only tail of the LRU list otherwise we scan the whole LRU list. */
buf_LRU_scan_and_free_block第一轮会迭代innodb_lru_scan_depth个对象,如果没有找到第二轮会迭代整个LRU list,参见buf_LRU_get_free_block完整代码
/******************************************************************//**
Returns a free block from the buf_pool. The block is taken off the
free list. If free list is empty, blocks are moved from the end of the
LRU list to the free list.
This function is called from a user thread when it needs a clean
block to read in a page. Note that we only ever get a block from
the free list. Even when we flush a page or find a page in LRU scan
we put it to free list to be used.
* iteration 0:
* get a block from free list, success:done
* if buf_pool->try_LRU_scan is set
* scan LRU up to srv_LRU_scan_depth to find a clean block
* the above will put the block on free list
* success:retry the free list
* flush one dirty page from tail of LRU to disk
* the above will put the block on free list
* success: retry the free list
* iteration 1:
* same as iteration 0 except:
* scan whole LRU list
* scan LRU list even if buf_pool->try_LRU_scan is not set
* iteration > 1:
* same as iteration 1 but sleep 10ms
@return the free control block, in state BUF_BLOCK_READY_FOR_USE */
buf_block_t*
buf_LRU_get_free_block(
/*===================*/
buf_pool_t* buf_pool) /*!< in/out: buffer pool instance */
{
buf_block_t* block = NULL;
bool freed = false;
ulint n_iterations = 0;
ulint flush_failures = 0;
bool mon_value_was = false;
bool started_monitor = false;
MONITOR_INC(MONITOR_LRU_GET_FREE_SEARCH);
loop:
buf_pool_mutex_enter(buf_pool);
buf_LRU_check_size_of_non_data_objects(buf_pool);
/* If there is a block in the free list, take it */
block = buf_LRU_get_free_only(buf_pool);
if (block != NULL) {
buf_pool_mutex_exit(buf_pool);
ut_ad(buf_pool_from_block(block) == buf_pool);
memset(&block->page.zip, 0, sizeof block->page.zip);
if (started_monitor) {
srv_print_innodb_monitor =
static_cast<my_bool>(mon_value_was);
}
block->skip_flush_check = false;
block->page.flush_observer = NULL;
return(block);
}
MONITOR_INC( MONITOR_LRU_GET_FREE_LOOPS );
freed = false;
if (buf_pool->try_LRU_scan || n_iterations > 0) {
/* If no block was in the free list, search from the
end of the LRU list and try to free a block there.
If we are doing for the first time we'll scan only
tail of the LRU list otherwise we scan the whole LRU
list. */
freed = buf_LRU_scan_and_free_block(
buf_pool, n_iterations > 0);
if (!freed && n_iterations == 0) {
/* Tell other threads that there is no point
in scanning the LRU list. This flag is set to
TRUE again when we flush a batch from this
buffer pool. */
buf_pool->try_LRU_scan = FALSE;
}
}
buf_pool_mutex_exit(buf_pool);
if (freed) {
goto loop;
}
if (n_iterations > 20
&& srv_buf_pool_old_size == srv_buf_pool_size) {
ib::warn() << "Difficult to find free blocks in the buffer pool"
" (" << n_iterations << " search iterations)! "
<< flush_failures << " failed attempts to"
" flush a page! Consider increasing the buffer pool"
" size. It is also possible that in your Unix version"
" fsync is very slow, or completely frozen inside"
" the OS kernel. Then upgrading to a newer version"
" of your operating system may help. Look at the"
" number of fsyncs in diagnostic info below."
" Pending flushes (fsync) log: "
<< fil_n_pending_log_flushes
<< "; buffer pool: "
<< fil_n_pending_tablespace_flushes
<< ". " << os_n_file_reads << " OS file reads, "
<< os_n_file_writes << " OS file writes, "
<< os_n_fsyncs
<< " OS fsyncs. Starting InnoDB Monitor to print"
" further diagnostics to the standard output.";
mon_value_was = srv_print_innodb_monitor;
started_monitor = true;
srv_print_innodb_monitor = true;
os_event_set(lock_sys->timeout_event);
}
/* If we have scanned the whole LRU and still are unable to
find a free block then we should sleep here to let the
page_cleaner do an LRU batch for us. */
if (!srv_read_only_mode) {
os_event_set(buf_flush_event);
}
if (n_iterations > 1) {
MONITOR_INC( MONITOR_LRU_GET_FREE_WAITS );
os_thread_sleep(10000);
}
/* No free block was found: try to flush the LRU list.
This call will flush one page from the LRU and put it on the
free list. That means that the free block is up for grabs for
all user threads.
TODO: A more elegant way would have been to return the freed
up block to the caller here but the code that deals with
removing the block from page_hash and LRU_list is fairly
involved (particularly in case of compressed pages). We
can do that in a separate patch sometime in future. */
if (!buf_flush_single_page_from_LRU(buf_pool)) {
MONITOR_INC(MONITOR_LRU_SINGLE_FLUSH_FAILURE_COUNT);
++flush_failures;
}
srv_stats.buf_pool_wait_free.add(n_iterations, 1);
n_iterations++;
goto loop;
}
问题:
什么Page是可以Replace的。
有没有可能一个用户线程free出来的page被其他用户线程用了?
https://mp.weixin.qq.com/s/o2OlvRiybIsqi7WU_Kvhiw
https://www.geek-share.com/detail/2706894441.html
网友评论