【MySQL高阶】18.缓冲池页管理
文章目录
- 5. InnoDB 内存结构
- 5.3 缓冲池 - Buffer Pool
- 5.3.3 缓冲池中的页是如何进行管理的?
- 5.3.3.1 内存中有这么多数据页如何快速找到目标页?
- 5.3.3.2 缓冲池中的数据放不下了怎么办?
- 5.3.4 缓冲池采用哪种淘汰策略?是如何实现的?
- 5.3.4.1 为什么要把页插入到中间而不是直接插入到新子列表的头部?
- 5.3.5 怎么查看当前缓冲池的信息?
5. InnoDB 内存结构
5.3 缓冲池 - Buffer Pool
5.3.3 缓冲池中的页是如何进行管理的?
当缓冲池初始化完成后,缓冲池中的数据页只是被分配了内存空间,并没有真实的数据(空闲页)
当用户进行数据查询时真实的数据从磁盘加载到内存中并分配一个内存中的数据页,这时内存中数据页的状态从空间变成了有实际的数据;(已使用页,干净页)
当用户修改数据时,并不是直接修改磁盘中的数据页,而是修改内存中数据页中的数据页,这时内存中数据页的状态从有实际数据变成了被修改。(已修改页,需要落盘,脏页)
在缓冲池中采用三个链表维护内存页,这三个链表也对应着内存中页的三种状态,分别是:
Free未使用的页,也可以称做空闲页;Clean已使用但未修改的页,也可以称做干净页;Dirty已修改的页,也可以称做脏页。对应的三个链表分别是
Free List 、 LRU List和Flush List:
Free List:只管理Free页LRU List:管理Clean页和Dirty页Flush List:只管理Dirty页如下图所示
Free List:管理着空闲的也就是没有被使用的内存页,当执行查询操作时,如果对应的页已经在buffer pool中则直接返回数据,如果没有且Free List不为空,则从磁盘中查询对应的数据并存到Free List的某一页中,然后把这个页从Free List中移除并放入LRU List中。LRU List:管理所有从磁盘中读取的数据页,包括未被修改的和已被修改的数据页,并根据LRU算法对链表中的页节点进行维护与淘汰。当数据库刚启动时LRU List是空的,这时从内存中申请到的页都存放在Free List中,当数据从磁盘读取到缓冲池时,首先从Free List中查找是否有可用的空闲页,如果有则把该页从Free List中删除并加入到LRU List;如果没有,则根据LRU算法淘汰LRU List末尾的页,并将该内存空间分配给新数据页;Flush List:当LRU List中的页被修改后会被标识为脏页(Dirty page),并把脏页加入到Flush List中,在这种情况下,数据库会通过刷盘机制把Flush List中的脏页刷回磁盘(从内存写到外存);Flush List是一个专门用来管理脏页的列表。脏页既存在于LRU List中,也存在于Flush List中,LRU List用来管理缓冲池中页的可用性,Flush List用来管理要被刷回磁盘的页,二者互不影响。Flush List中的脏页在执行了刷盘操作后会将空间还给Free List。
总结:
每个缓冲池都采用三个链表维护内存页,这三个链表也对应着内存中页的三种状态,分别是:
Free未使用的页,也可以称做空闲页;Clean已使用但未修改的页,也可以称做干净页;Dirty已修改的页,也可以称做脏页。
5.3.3.1 内存中有这么多数据页如何快速找到目标页?
首先第一种办法是通过遍历,这种做法显示不能满足性能要求
InnoDB采用的是Page Hash的方式,也就是每当把磁盘中数据页加载到内存时,用数据页的表空间Id和页号做为Key,当前页在内存中的地址做为Value保存起来,每次查询时就可以通过Key快速定位到目标页,如果内存中没有目标页,则从磁盘中获取。
5.3.3.2 缓冲池中的数据放不下了怎么办?
InnoDB根据根据自身的实际场景,使用淘汰策略来淘汰相应的数据页,从而释放出内存空间,以便新的数据页加载到内存中。
5.3.4 缓冲池采用哪种淘汰策略?是如何实现的?
- 缓冲池淘汰策略采用变形的最近最少使用(
LRU)算法(在原来LRU算法的基础做了修改),以下出现的LRU算法指的是LRU变形算法。- 缓冲池使用
LRU算法管理链表,当有新页面添加到缓冲池时,最近最少使用的页将被淘汰,并将新页添加到列表的中间,这种中点插入策略将列表视为两个子列表:
- 链表头部,是存放最近访问的新页(年轻页)子列表;
- 链表尾部,是存放最近较少访问的旧页子列表。
如下图所示:
- 经常使用的页保存在新子列表中,较少使用的页保存在旧子列表中,随着时间的推移,旧子列表中的页将会逐渐被淘汰。默认情况下,算法的执行过程如下:
- 缓冲池总容量的
5/8用于新子列表,3/8用于旧子列表;- 列表的中间插入点是新子列表的尾部与旧子列表头部的交界;
- 当一个页被读入缓冲池时,首先插入到中点做为旧子列表的头节点;
- 当访问的页在旧子列表中时,把被访问的页移动到新子列表的头部,使其成为 “新” 页;
- 数据库运行的过程中,缓冲池中被访问页面的位置不断更新,未访问的页面向列表的尾部移动,从而逐渐"变老" ,最终超出缓冲池容量的页从旧子列表的尾部被淘汰。
总结:
缓冲池淘汰策略采用变形的最近最少使用(
LRU)算法
5.3.4.1 为什么要把页插入到中间而不是直接插入到新子列表的头部?
因为
InnoDB在读取页时,可能会发生"预读",预读的意思是InnoDB根据当前访问的记录自动推断后面可能会访问哪个页,并把他们提前加载到内存中,从而提高以后查询的效率,预读的页以并不一定会被真正的读取,从中间点插入可以使其尽快被淘汰。因为有的预读的部分可能用不到,如果放到前面,那么那些读不到的就会占用之前读到的应占据的位置。放到中间可以保证之前读到的应占据的位置还在那里,哪些用不到的部分往后直到回收。
也就是说,中间插入的新数据,被使用部分前移到新子列表,不使用部分默认就在旧子列表。
5.3.5 怎么查看当前缓冲池的信息?
通过使用
SHOW ENGINE InnoDB STATUS访问InnoDB标准监视器输出中BUFFER POOL AND MEMORY部分查看有关缓冲池的指标。缓冲池指标位于InnoDB标准监视器输出的缓冲池和内存部分:
mysql> SHOW ENGINE INNODB STATUS\G *************************** 1. row *************************** Type: InnoDB Name: Status: ===================================== .......省略 ---------------------- BUFFER POOL AND MEMORY # 缓冲池和内存 ---------------------- Total large memory allocated 0 # 为缓冲池分配的总内存,以字节为单位 Dictionary memory allocated 634151 # 为InnoDB数据字典分配的总内存,以字节为单位 Buffer pool size 8192 # 分配给缓冲池的总页大小 Free buffers 6917 # 缓冲池空闲列表的总页大小 Database pages 1270 # 缓冲池LRU列表的总分页大小 Old database pages 487 # 缓冲池旧子列表的总页大小 Modified db pages 0 # 缓冲池中当前修改的页数(脏页) Pending reads 0 # 等待读入缓冲池的页数 Pending writes: LRU 0, flush list 0, single page 0 Pages made young 0, not young 0 0.00 youngs/s, 0.00 non-youngs/s Pages read 1094, created 176, written 736 0.00 reads/s, 0.00 creates/s, 0.00 writes/s No buffer pool page gets since the last printout Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s LRU len: 1270, unzip_LRU len: 0 I/O sum[0]:cur[0], unzip sum[0]:cur[0] -------------- ROW OPERATIONS -------------- 0 queries inside InnoDB, 0 queries in queue 0 read views open inside InnoDB Process ID=2658071, Main thread ID=139983131321920 , state=sleeping Number of rows inserted 75, updated 0, deleted 0, read 51 0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s Number of system rows inserted 166, updated 409, deleted 8, read 6079 0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s ---------------------------- END OF INNODB MONITOR OUTPUT ============================ 1 row in set (0.00 sec) mysql>InnoDB缓冲池指标如下,以供参考:
小结
- 缓冲池是用来缓存各种数据,最主要的就是缓存从磁盘中加载的数据页,从而提升效率
- 缓冲池为了方便数据组织定义了不同的数据结构,包括
Instances实例,Chunk块,其中Buffer Pool中包含至少一个Instances,Instances包含至少一个Chunk块,Chunk管理若干个从磁盘加载到内存的Page数据页- 缓冲池能过三个链表管理内存中的数据页,分别是
Free List 、 LRU List和Flush List- 缓冲池淘汰策略采用变形的最近最少使用(
LRU)算法
