上图是MySQL的整体架构和InnoDB存储引擎的架构,本篇文章来谈一下Buffer Pool(缓冲池)
为什么需要BufferPool?
由于对数据库CRUD是非常频繁的,如果直接在磁盘上更新操作数据的话,对磁盘进行随机读写,消耗是相当大的。所以需要在内存中开辟一个池子用来对数据进行暂时的读写操作,也就是Buffer Pool。当在内存中操作完毕后,需要在合适的机会将内存中的数据刷到磁盘上去,否则就是导致内存与磁盘的数据不一致,也就是脏数据。
BufferPool整体结构
由上图的左方可知,BP内的结构为一个一个不同颜色的小方块构成,这些小方块就是数据页,不同颜色深度的方块代表着不同类型的数据页。然后还有自适应哈希(Adaptive Hash Index)和Change Buffer。
一个数据页默认大小事16KB,BP的默认大小是128MB,对于每个缓存的数据页,还会有一个描述信息,这个描述页是用来描述数据页所属的表空间、数据页的编号等等。
如何管理数据页
上边提过不同颜色深度的数据页其实是表示了不同的类型,那么这些不同类型的数据页BP是如何管理的?比如BP满了后如何清理?等等
free list
free链表是双向链表,用来管理那些空闲的数据页,这样当从磁盘往内存加载数据的时候,可以从free链表中选择合适的空白数据页。这个加载数据的过程如下图
flush list
脏数据的概念在前边已经提过了,那么怎么知道哪些数据页的数据是需要刷新到磁盘上的?flush链表就是管理这些脏页的。内部是按照时间进行排序的。但是脏页不仅仅在flush链表上的,下方的lru链表中可能也有脏页,但是刷盘操作只在flush链表上。
lru list
表示正在使用的缓冲区,管理clean page和dirty page,缓冲区以 midpoint为基点,前面链表称为new列表区,存放经常访问的数据,占63%;后 面的链表称为old列表区,存放使用较少数据,占37%。
改性LRU:链表分为new和old两个部分,加入元素时并不是从表头插入,而是从中间 midpoint位置插入,如果数据很快(设定时间)被访问,那么page就会向new列表头部移动,如果数据没有被访问,会逐步向old尾部移动,等待淘汰。
自适应索引哈希
自适应哈希索引,用于优化对BP数据的查询。InnoDB存储引擎会监控对表索引的查找,如果观察到建立哈希索引可以带来速度的提升,则建立哈希索引,所以称之为自适应。InnoDB存储引擎会自动根据访问的频率和模式来为某些页建立哈希索引。
刷盘机制
后台有专门的线程每隔一段时间负责把脏页刷新到磁盘,这样可以不影响用户线程处理正常的请求。主要有两种刷新路径:
- 从
LRU链表
的冷数据中刷新一部分页面到磁盘。
后台线程会定时从LRU链表
尾部开始扫描一些页面,扫描的页面数量可以通过系统变量innodb_lru_scan_depth
来指定,如果从里边儿发现脏页,会把它们刷新到磁盘。这种刷新页面的方式被称之为BUF_FLUSH_LRU
。 - 从
flush链表
中刷新一部分页面到磁盘。
后台线程也会定时从flush链表
中刷新一部分页面到磁盘,刷新的速率取决于当时系统是不是很繁忙。这种刷新页面的方式被称之为BUF_FLUSH_LIST
。
有时候后台线程刷新脏页的进度比较慢,导致用户线程在准备加载一个磁盘页到Buffer Pool
时没有可用的缓存页,这时就会尝试看看LRU链表
尾部有没有可以直接释放掉的未修改页面,如果没有的话会不得不将LRU链表
尾部的一个脏页同步刷新到磁盘(和磁盘交互是很慢的,这会降低处理用户请求的速度)。这种刷新单个页面到磁盘中的刷新方式被称之为BUF_FLUSH_SINGLE_PAGE
。
当然,有时候系统特别繁忙时,也可能出现用户线程批量的从flush链表
中刷新脏页的情况,很显然在处理用户请求过程中去刷新脏页是一种严重降低处理速度的行为(毕竟磁盘的速度满的要死),这属于一种迫不得已的情况,不过这得放在后边唠叨redo
日志的checkpoint
时说了。