mysql缓冲池源码分析之如何加载数据页

最近又开始看了mysql源码,顺便在这里总结一下自己的理解。
为了简单理解,我就简单描述了一下流程。
流程如下:
1.是否命中缓冲池---->命中直接访问(为了避免查询数据页时扫描LRU,还为每个buffer pool instance维护了一个page hash,通过space id和page no查找对应的page。一般情况下,当我们需要读入一个Page时,首先根据space id和page no找到所属的buffer pool instance。然后根据page hash查询,如果page hash中没有查询到,则表示需要从磁盘读取。)
2.没有命中---->从磁盘加载页到缓冲池中–→需要一个空闲页面---->有的话直接加入
3.free list为空–→需要访问LRU进行淘汰
4.LRU没有可以淘汰的---->单页刷脏–→然后释放内存块加入到free list–→去free list去取

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);  //加mutex锁

	buf_LRU_check_size_of_non_data_objects(buf_pool);//检查ahi,lock所占用的buffer pool是否guo'gao

	/* If there is a block in the free list, take it */
	block = buf_LRU_get_free_only(buf_pool);  //这个函数从free list拿出来一个干净页,拿不到返回null,拿到返回block

	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(mon_value_was);
		}

		block->skip_flush_check = false;
		block->page.flush_observer = NULL;
		return(block);
	}

	MONITOR_INC( MONITOR_LRU_GET_FREE_LOOPS );

	freed = false;

	//重复进行空闲页扫描,根据LRU list对页面进行淘汰 分为common 和 unzipped LRU
	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) {
		//如果循环获取空闲页的次数大于20次,系统将发出报警信息
		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. */
	
	//当page_cleaner线程的free速度不足以跟上工作量的时候,即没有可replace的页时。
	//buf_flush_single_page_from_LRU 这个函数是进行一个单页的刷脏,如果逐出成功,返回true
	//所以失败的值不会增加

	if (!buf_flush_single_page_from_LRU(buf_pool)) {
		MONITOR_INC(MONITOR_LRU_SINGLE_FLUSH_FAILURE_COUNT);
		++flush_failures;
	}

	//所以 free 链表的值加一。
	srv_stats.buf_pool_wait_free.add(n_iterations, 1);

	//迭代次数增加
	n_iterations++;

	goto loop;
}

你可能感兴趣的:(msyql源码)