什么是buffer pool
MySQL服务器启动的时候会向操作系统申请了一片连续的内存作为缓冲池(buffer pool),默认128M,可以通innodb_buffer_pool_size过来调整它的大小。用于缓存表数据与索引数据,把磁盘上的数据加载到缓冲池,避免每次访问都进行磁盘IO,起到加速访问的作用。
buffer pool中存有控制块和缓存页一一对应,控制块包含:缓存页所属的表空间编号、页号、缓存页在buffer pool中的地址、链表节点信息、一些锁信息以及LSN信息;缓存页和磁盘上默认的页大小一样都是16K。
预读
MySQL是以页(16K)为单位读取磁盘数据放入buffer pool的,按页读取不仅能提高性能,还遵守“集中读写”的原则能把一些“可能要访问”的页提前加入缓冲池,避免未来的磁盘IO操作。
LRU链表
MySQL是用LRU(Least recently used)算法来管理这些缓存页的。并将LRU链表分为young和old两个区域。首次从磁盘上加载到buffer pool的页放到老年代头部,如果数据被访问则加到新生代的头部;如果数据没被访问则会被逐渐移到老年代尾部直到淘汰。
注意:在一定时间内访问初次加载到buffer pool的页不会被加到新生代的头部,这个时间由innodb_old_blocks_time变量控制。这是针对全表扫描时,短时间内访问大量使用频率非常低的页面情况的优化。
flush链表
如果修改了buffer pool中某个缓存页的数据,那它就和磁盘上的页不一致了,这样的缓存页被称为脏页(dirty page)。
buffer pool内创建一个存储脏页的链表,凡是修改过的缓存页对应的控制块都会作为一个节点加入到一个链表中,因为这个链表节点对应的缓存页都是需要被刷新到磁盘上的,所以也叫flush链表。
刷盘
后台有专门的线程每隔一段时间负责把脏页刷新到磁盘,这样可以不影响用户线程处理正常的请求。主要有两种刷新路径:
- 从LRU链表的冷数据中刷新一部分页面到磁盘:后台线程会定时从LRU链表尾部开始扫描一些页面,如果从里边儿发现脏页,会把它们刷新到磁盘。这种刷新页面的方式被称之为BUF_FLUSH_LRU。
- 从flush链表中刷新一部分页面到磁盘:后台线程也会定时从flush链表中刷新一部分页面到磁盘,刷新的速率取决于当时系统是不是很繁忙。这种刷新页面的方式被称之为BUF_FLUSH_LIST。
当后台线程刷新脏页的进度比较慢,处理用户请求时,没有可用空间加载新的缓存页时,会在线刷盘,降低处理用户请求的速度。