转自:http://blog.csdn.net/wyzxg/article/details/7700394
其他参考:
《高性能MySQL》 - 8.4.5 InnoDB缓冲池
《MySQL技术内幕InnoDB存储引擎》(第二版内容有所更新) - 2.3 InnoDB体系结构
##############################################
书中是先对后台线程进行说明,然后对内存部分进行说明,这样更好理解innoDB引擎内存池在使用时的过程。
【后台线程】
InnoDB有多个内存块,可以认为这些内存块组成了一个大的内存池,负责如下工作:
维护所有进程/线程需要访问的多个内部数据结构。
缓存磁盘上的数据,方便快速的读取,并且在对磁盘文件的数据进行修改之前在这里缓存。
重做日志(redo
log)缓冲。
后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据。此外,将已修改的数据文件刷新到磁盘文件,同时保证在数据库发生异常情况下InnoDB能恢复到正常运行状态。
默认情况下,InnoDB存储引擎的后台线程有7个,4个IO thread,1个master thread,1个锁(lock)监控线程,1个错误监控线程。IO thread的数量由配置文件中的innodb_file_io_threads参数控制,默认为4,可以通过show engine innodb status \G查看IO thread,例如:
mysql> show engine innodb status \G *************************** 1. row *************************** Type: InnoDB Name: Status: ===================================== ... -------- FILE I/O -------- I/O thread 0 state: waiting for i/o request (insert buffer thread) I/O thread 1 state: waiting for i/o request (log thread) I/O thread 2 state: waiting for i/o request (read thread) I/O thread 3 state: waiting for i/o request (read thread) I/O thread 4 state: waiting for i/o request (read thread) I/O thread 5 state: waiting for i/o request (read thread) I/O thread 6 state: waiting for i/o request (write thread) I/O thread 7 state: waiting for i/o request (write thread) I/O thread 8 state: waiting for i/o request (write thread) I/O thread 9 state: waiting for i/o request (write thread)
可以看到上面IO线程中的四种分别是insert buffer thread、log thread、read thread、write thread。MySQL 5.5可以对IO thread的read thread、write thread的数量进行配置,默认的read thread、write thread分别增大到4个,默认的insert buffer thread、log thread仍为一个线程,上面也是MySQL 5.5版本的配置,同时不再使用innodb_file_io_threads参数,而是分别使用innodb_read_io_thread和innodb_write_io_thread参数,此参数可根据CPU核数、磁盘IO性能进行调整,如果将read thread或write thread配置很大但实际服务器性能不能满足,会导致线程请求积压,反而会降低性能。
【内存】
InnoDB存储引擎内存由以下几个部分组成:缓冲池(buffer pool)、重做日志缓冲池(redo log buffer)以及额外的内存池(additional memory pool),分别由配置文件中的参数innodb_buffer_pool_size和innodb_log_buffer_size的大小决定。
##############################################
mysql buffer pool里的三种链表和三种page
buffer pool是通过三种list来管理的
1) free list
2) lru list
3) flush
list
buffer
pool中的最小单位是page,在innodb中定义三种page
1) free page
:此page未被使用,此种类型page位于free链表中
2) clean
page:此page被使用,对应数据文件中的一个页面,但是页面没有被修改,此种类型page位于lru链表中
3) dirty
page:此page被使用,对应数据文件中的一个页面,但是页面被修改过,此种类型page位于lru链表和flush链表中
buffer pool flush list的工作原理
dirty
page如何存在flush链表中?
在flush list中存在的page只能是dirty page,flush list中存在的dirty
page是按着oldest_modification时间排序的,当页面访问/修改都被封装为一个mini-transaction,mini-transactin提交的时候,则mini-transaction涉及到的页面就进入了flush链表中,oldest_modification的值越大,说明page越晚被修改过,就排在flush链表的头部,oldest_modification的值越小,说明page越早被修改过,就排在flush链表的尾部,这样当flush链表做flush动作时,从flush链表的尾部开始scan,写出一定数量的dirty
page到磁盘,推荐checkpoint点,使恢复的时间尽可能的短。除了flush链表本身的flush操作可以把dirty
page从flush链表删除外,lru链表的flush操作也会让dirty page从flush链表删除。
buffer pool lru list的工作原理
总的来说每当一个新页面被读取buffer
pool之后,MySQL数据库InnoDB存储引擎都会判断当前buffer pool的free page是否足够,若不足,则尝试flush
LRU链表。
在MySQL 5.6.2之前,用户线程在读入一个page
(buf_read_page)、新建一个page(buf_page_create)、预读page(buf_read_ahead_linear)
等等操作时,都会在操作成功之后,调用buf_flush_free_margin函数,判断当前buffer pool是否有足够的free
pages,若free pages不足,则进行LRU list flush,释放出足够的free pages,保证系统的可用性。
通过判断当前buf pool中需要flush多少dirty pages,才能够预留出足够的可被替换的页面(free pages or
clean pages in LRU list tail)。
说明:
可用pages由以下两部分组成:
1. buf
pool free list中的所有page,都是可以立即使用的。
2. buf pool LRU
list尾部(5+2*BUF_READ_AHEAD_AREA)所有的clean
pages。
其中:BUF_READ_AHEAD_AREA为64,是一个linear read ahead读取的大小,1 extent
由于buf_flush_free_margin函数是在用户线程中调用执行的,若需要flush LRU
list,那么对于用户的响应时间有较大的影响。因此,在MySQL 5.6.2之后,InnoDB专门开辟了一个page
cleaner线程,处理dirty page的flush动作(包括LRU list flush与flush list flush),降低page
flush对于用户的影响。
在MySQL 5.6.2前后的版本中,LRU list
flush的不同之处在于是由用户线程发起,还是有后台page cleaner线程发起。但是,无论是用户线程,还是后台page
cleaner线程,再决定需要进行LRU list flush之后,都会调用buf_flush_LRU函数进行真正的flush操作。
不同之处在于,MySQL 5.6.2之前,用户线程调用的buf_flush_free_margin函数,在判断是否真正需要进行LRU
list flush时,将LRU list tail部分的clean
pages也归为可以被replace的pages,不需要flush。而在page cleaner线程中,每隔1s,无论如何都会进行一次LRU
list flush调用,无论LRU list
tail中的page是否clean。这也可以理解,用户线程,需要尽量降低flush的概率,提高用户响应;而后台线程,尽量进行flush尝试,释放足够的free
pages,保证用户线程不会堵塞。
Buffer Pool LRU/Flush List
flush对比
1).LRU list flush,由用户线程触发(MySQL 5.6.2之前);而Flush list
flush由MySQL数据库InnoDB存储引擎后台srv_master线程处理。(在MySQL 5.6.2之后,都被迁移到page
cleaner线程中)
2).LRU list flush,其目的是为了写出LRU 链表尾部的dirty page,释放足够的free
pages,当buf pool满的时候,用户可以立即获得空闲页面,而不需要长时间等待;Flush list
flush,其目的是推进Checkpoint LSN,使得InnoDB系统崩溃之后能够快速的恢复。
3).LRU list
flush,其写出的dirty page,需要移动到LRU链表的尾部(MySQL 5.6.2之前版本);或者是直接从LRU链表中删除,移动到free
list(MySQL 5.6.2之后版本)。Flush list flush,不需要移动page在LRU链表中的位置。
4).LRU list
flush,由于可能是用户线程发起,已经持有其他的page latch,因此在LRU list flush中,不允许等待持有新的page
latch,导致latch死锁;而Flush list flush由后台线程发起,未持有任何其他page
latch,因此可以在flush时等待page latch。
5).LRU list flush,每次flush的dirty
pages数量较少,基本固定,只要释放一定的free pages即可;Flush list
flush,根据当前系统的更新繁忙程度,动态调整一次flush的dirty pages数量,量很大。
buffer pool free
list工作原理
free链表里存放的是空闲页面,初始化的时候申请一定数量的page,在使用的过程中,每次成功load页面到内存后,都会判断free
page是否够用,如果不够用的话,就flush lru链表和flush链表来释放free
page,这就可以满足其他进程在申请页面,使系统可用。