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链表。
写缓冲(change buffer)
当要写的数据页不在缓冲池时,将缓冲变更(buffer changes)记录在缓冲池,未来数据被读取时,再将数据合并(merge)恢复到缓冲池中。
写缓冲只适用非唯一索引,因为唯一索引需要检查唯一性。
刷盘
后台有专门的线程每隔一段时间负责把脏页刷新到磁盘,这样可以不影响用户线程处理正常的请求。主要有两种刷新路径:
- 从LRU链表的冷数据中刷新一部分页面到磁盘:后台线程会定时从LRU链表尾部开始扫描一些页面,如果从里边儿发现脏页,会把它们刷新到磁盘。这种刷新页面的方式被称之为BUF_FLUSH_LRU。