三. 缓冲池 buffer pool
Innodb引擎会在mysql启动的时候,向操作系统申请一块连续的空间当做buffer pool,空间的大小由变量innodb_buffer_pool_size
确定。(大小单位是k)
1.内部结构
buffer pool的内部结构是由缓冲页和控制块组成的。
- 【缓冲页】:buffer pool中的数据页我们称之为缓冲页,是和磁盘上的数据一一对应的,大小为16KB,磁盘上的数据加载到buffer pool中,是一个完整的页,不会存在1.5个页。
- 【控制块】:控制块是缓冲页的描述信息,内部保存的是一个缓冲页所属的表空间号,数据页编号,数据页地址。每个控制块的大小为缓冲页的5%左右,大概为800个字节。每一个缓冲页都有一个控制块对应。
【小知识】:buffer pool的初始化
数据库会在启动的时候,按照配置中的Buffer Pool大小,去向操作系统申请一块内存,作为Buffer Pool的内存区域,然后会按照默认的缓存页的的大小【16KB】以及对应的【800个字节左右】的【控制块】的大小,在Buffer Pool中划分出一个一个的缓存页和一个一个与其对应的描述数据(控制块)。此时的buffer pool像一个干净的本子,没有书写任何内容。
2.free链
刚刚初始化的buffer pool 里面全部都是空的缓冲页,当程序不断执行,会有新的缓冲页被加载到内存中去,那么要该怎么样去判断哪些是空闲的缓冲页呢?InnoDB的设计中,会将所有空闲的缓冲页的控制块作为一个一个的节点,形成一个链表,这个链就是free链。
free链是由一个基节点,和很多个控制块组成。基节点中存放着头节点、尾节点指针,还有节点个数。控制块中包含着节点的pre和next指针,这也意味着free链是一个双向链表。
当加载数据时,会从free链中找到一个空闲页,把数据页的信息写入控制块,并把该数据页对应的控制块从free链中移除。
(1).如何判断数据页是否被缓存?
在buffer pool中会把表空间号和页号作为key,控制块地址作为value,设计成一个hash表,每次查询时只需要通过表空间好和页号作为key去hash表中查询,如果有就能顺着控制块找到缓冲页。如果没有就直接去表空间中去寻找,查询到数据之后再从free链中找到一个空闲页缓存起来。这样就大大提升了查询效率。
3.flush链表
(1).脏页
从前面buffer pool的知识可以知道,buffer pool中的数据页是从磁盘中缓存到内存中去的,此时的数据页和磁盘中的数据页中的数据是相同的。但是每一条sql的执行都是优先去buffer pool中执行的,此时buffer pool中的数据就和磁盘中的数据产生了数据差异,有数据差异的数据页就叫做脏页。
(2).flush链
flush链和free链相似,flush链也是一个双向链表,其内部也有一个基节点和多个控制块,不过不同的是flush链中的控制块对应的是被修改过的缓存页。
(3).刷盘时机
后台会有专门的线程每隔一段时间就把flush链中的脏页刷新到磁盘中去,刷新的频率取决于当前系统是否繁忙。这种情况下当系统崩溃就会产生数据丢失,MySQL采用了日志系统来应对这一情况。
4.LRU链表
该部分内容仅作了解即可
(1).概述
我们的内存是有限的,buffer pool的大小也是有限的,缓存只是作为数据的中转站,当数据量足够大时buffer pool的容量就会被占满,此时我们需要新的缓存页时该怎么办,最合理的解决办法就是,在需要新的缓存页时,把旧的,使用频率低的缓存页给干掉,加入新的缓存页。这就是典型的LRU(Least Recently Used)算法。当客户端访问一条数据时,会加载对应的数据页到buffer pool,并会将缓冲页对应的控制块放置到【LRU链表的首位】。一旦buffer pool被占满,则从链表的末端开始淘汰数据,这是最简单的实现。
(2).优化
innodb对该链表进行了优化,将【LRU链表】分成了两个区域,分为【热数据区】和【冷数据区】,默认情况下冷数据区占了总链表的37%。
-
对于预读的数据页,会在第一次访问时放入old区域,如果在sql执行的过程中访问相邻数据时,再次访问访问到该数据页,则把他加入如热数据区。
-
【大表的全表扫描】是个使用频率很低的操作(小表怎么操作都无所谓),但是如果按照上边的操作,首先全表数据会被放在【old区】,全表扫描必然会因为访问相邻数据而产生第二次、第三次、甚至数百次的访问,也就以为着这些页面会被全部放在young区。为了解决这个问题,INnodb提供了这样一个参数【innodb_old_blocks_time】,默认是1s,他的执行流程大致如下:
- 1、页被首次访问时会记录访问的时间戳。
2、以后访问都和首次访问的时间进行对比,如果时间大于1s,就讲当前页放入yong区。
3、一个sql的扫描一个页的时间,哪怕在慢也不会低于1s,这样就解决了一个全表扫秒而导致全表成为热点数据的问题。