innodb的buffer pool原理
Posted wen-pan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了innodb的buffer pool原理相关的知识,希望对你有一定的参考价值。
说明:下文基于innodb存储引擎介绍!!!
一、什么是buffer pool
- 我们知道对于mysql数据库,是将数据以页的方式存放在磁盘上的。我们也知道对于磁盘的操作是非常慢的,如果我们需要查询某些数据时CPU都是从磁盘上去将这些数据读取出来然后返回给我们,那么这种效率肯定是低的让人奔溃的。那么MySQL采用了什么手段来提高数据读取效率呢?这就是
buffer pool
技术。顾名思义buffer pool
就是内存缓冲池的意思,也就是使用内存来提高CPU和磁盘的交互效率。 - 在MySQL启动时就会向操作系统申请一片连续的内存空间,MySQL的设计者给这片空间起了个名字叫
buffer pool
(实际上就是一块连续的内存空间) - buffer pool的大小可以由我们自己通过mysql启动参数
innodb_buffer_pool_size
调整。当我们设置innodb_buffer_pool_size
值为1g的时候就意味着MySQL启动的时候会向操作系统申请1g的内存空间来当做buffer pool
。
二、buffer pool内部构成
我们知道了buffer pool是一片连续的内存空间,那么这片内存空间内部是怎么构成的呢?
我们知道MySQL的数据在磁盘上是按照页来管理的,那么这些磁盘上的页数据被加载到buffer pool中也应该是按照页来对应的。buffer pool对应着的一片连续的内存被划分为若干个页面,一个页面大小与innodb表空间的页面大小一致,默认都是16kb。并且为了更好的管理这些buffer pool的页面,MySQL的设计者还为每个缓冲页面创建了一个控制块
(这个控制块用来记录该缓冲页在buffer pool中的内存地址、缓存的是哪个表空间的哪个页面,链表节点信息等。也可以理解为一个控制块
就记录着一个缓冲页面的元数据),后面对于缓冲页面的操作只需要通过这个控制块
即可快速的定位到对应的缓冲页。图示如下:
- 比如buffer pool被分为n个缓冲页面,那么也就对应着n个
控制块
来表示这些缓冲页
三、buffer pool整体结构
- buffer pool被分为一个个page,使用hash表 + 多个链表来管理这些page
- 空闲链表中存放的是空闲的缓冲页对应的
[控制块]
- Lru链表中存放的是已使用的缓冲页对应的
[控制块]
- 脏页链表中存放的是已经被修改过的缓冲页对应的
[控制块]
- hash表中key由
[表空间号 + 磁盘页号]
组成,value表示该[表空间号 + 磁盘页号]
对应的数据存放在buffer pool中的哪个页的地址(准确的说应该是该缓冲页的控制块的地址,因为可以通过控制块快速的定位到缓冲页)。
注意:这些链表节点以及hash表并不会占据buffer pool中的空间,而是在MySQL启动时会额外的向操作系统申请一部分内存空间来存放这些节点以及hash表!!!!
四、三种链表功能介绍
我们知道buffer pool也是以页的方式来管理,buffer pool中有很多页,那么MySQL是如何区分[哪些缓冲页是空闲的,哪些缓冲页是已经被使用的,哪些缓冲页已经被修改过 ]
?
所以MySQL为了更好的区分和管理buffer pool中的缓冲页,使用了三个链表来记录[ 哪些缓冲页是空闲的,哪些缓冲页是已经被使用的,哪些缓冲页已经被修改过 ]
这些页面。
1、空闲链表(free链表)
为了方便的管理buffer pool中的每个缓冲页面,每个缓冲页面都会用一个控制块
来表示该缓冲页的相关信息。在MySQL服务器刚启动好的时候,此时还没有任何数据被加载到buffer pool中,这些控制块会被串成一个双向链表。表示这些控制块
对应的缓冲页面还没有被使用。
2、LRU链表
简单理解:用来存放已经被使用的缓冲页对应的控制块
的链表被称为LRU链表
当我们执行一条查询语句(比如:select * from tbl where id = 1
)此时存储引擎就会根据我们的查询条件去查找id=1的这条记录,并且将这条记录在磁盘上对应的页面加载到buffer pool
中(注意:这里虽然是只查询了id = 1 的一条数据,但是MySQL是以page为单位来管理数据的,所以也会将id=1所在的页面的所有数据都加载到buffer pool中)。那么加载buffer pool中的数据应该放在哪个缓冲页呢?????
- 此时MySQL会去空闲链表上取一个
控制块
(并将这个控制块从[空闲链表]
上删除,表示这个缓冲页已经被使用了),将这个取出来的控制块
重新组成一个链表,这个链表就是LRU链表
。 - 注意:一个缓冲页要么就位于
[空闲链表]
要么就位于[lru链表]
上,不可能有重合。因为[空闲链表上的节点]
代表还没有被使用的缓冲页的控制块,而[LRU链表上的节点]
表示已经被使用的缓冲页的控制块,所以不可能存在某个缓冲页又被使用又没有被使用的情况。
3、脏页链表(flush链表)
当执行update语句去更新缓冲页中的数据时,一旦缓冲页中的数据被更新,那么这个页面的数据就和磁盘上的数据不一致了,此时这个缓冲页就被称为[脏页]
,这些脏页需要被刷写到磁盘中,以保证缓冲页的数据和磁盘的数据一致。那么MySQL如何才能快速的从众多缓冲页面中精确的查找到哪些页面是脏页呢?
MySQL依然采用链表的方式来记录这些脏页,在buffer pool
中一旦某个页面的数据被修改,那么MySQL会将这个页面标记为脏页,并创建一个链表将这个页面串联起来(这就是[脏页链表]
),当MySQL需要将buffer pool中的脏页刷写到磁盘上的时候,就可以从该链表头开始依次找到这些脏页,然后刷写到磁盘。
五、hash表功能介绍
思考一个问题:在我们执行查询语句select * from tbl where id = 1
时MySQL是如何判断是否id=1
对应的数据页已经从磁盘上加载到内存中了???这就用到了hash表
- 当执行查询时,存储引擎根据我们的SQL的查询条件定位到需要查询的数据位于磁盘的哪个表空间的哪个页,得到
表空间号
和磁盘上的页号
- 然后根据上面得到的
[表空间号 + 页号]
作为key,计算该key的hash值,然后通过该hash值去hash表中查找,通过查找结果判断该数据页是否已经从磁盘上加载到buffer pool中了 - 如果找到了,则说明该页已经被加载到buffer pool中了,此时只需要通过hash表上该key对应的value的值(value值表示该缓冲页对应的
控制块
)去LRU链表
上找到对应的缓冲页面,然后从缓冲页面中获取值。 - 如果找不到,则说明磁盘上的该页号对应的数据页还没有被加载到内存中,那么此时便会去磁盘中加载该页的数据到buffer pool中。并且将
[表空间号 + 页号]
作为key,将该磁盘页被放置在buffer pool中的地址作为value添加到hash表中,下次再访问磁盘上的该页就会直接使用buffer pool中缓存的页数据了
六、buffer pool工作流程
1、数据查询流程
- MySQL启动,申请一块连续的内存空间作为buffer pool (buffer pool被分为一个个page,每个page的信息由一个
控制块
表示) - 由于此时还没有执行任何查询,所以此时buffer pool的所有页都是空闲的,这些缓冲页对应的
控制块
组成了一个空闲链表 - 当执行查询时,存储引擎根据我们的SQL的查询条件定位到需要查询的数据位于磁盘的哪个
表空间
的哪个页
- 然后根据上面得到的
[表空间号 + 页号]
作为key,计算该key的hash值,然后通过该hash值去hash表中查找,通过查找结果判断该数据页是否已经从磁盘上加载到buffer pool中了- 如果找到了,则说明该页已经被加载到buffer pool中了,此时只需要通过hash表上该key对应的value的值(value值表示该缓冲页对应的
控制块
)去LRU链表
上找到对应的缓冲页面,然后从缓冲页面中获取值。并且会将该缓冲页对应的控制块
移动到LRU链表的头部(冷热LRU情况另说) - 如果找不到,则说明磁盘上的该页号对应的数据页还没有被加载到内存中,那么此时便会去磁盘中加载该页的数据到buffer pool中。并且将
[表空间号 + 页号]
作为key,将该磁盘页被放置在buffer pool中的地址作为value添加到hash表中,下次再访问磁盘上的该页就会直接使用buffer pool中缓存的页数据了
- 如果找到了,则说明该页已经被加载到buffer pool中了,此时只需要通过hash表上该key对应的value的值(value值表示该缓冲页对应的
- 磁盘上新加载到buffer pool中的页数据应该放在buffer pool中的那个缓冲页中呢?
- 此时MySQL会从
空闲链表
中选取一个控制块
,并将刚加载到内存的页面存放到这个控制块对应的缓冲页面中。 - 并且将这个
控制块
从空闲链表中删除(表示这个缓冲页已经被使用) - 同时,将这个控制块添加到LRU链表中(表示这个缓冲页已经被使用)
- 此时MySQL会从
2、脏页更新流程
如果对于LRU链表上的控制块对应的缓冲页有更新操作,那么该缓冲页中的数据和磁盘上的数据就不一致了,此时该缓冲页就被叫做脏页。
- 当我们执行更新语句
update tbl set name = 'wenpan' where id = 1
- 此时id = 1 对应着的缓冲页(假设叫
缓冲页x
)被更新- 注意:如果此时id = 1的数据还没有被加载到buffer pool中,那么会先通过select查询,将id = 1的数据页从磁盘加载到buffer pool中然后再进行更新。
- 在
缓冲页x
被更新时innodb会创建一个节点并添加到脏页链表
中 - 最后由innodb的脏页更新机制,触发对
脏页链表
中的脏页进行刷写到磁盘,保证磁盘数据和buffer pool中的数据一致性。
以上是关于innodb的buffer pool原理的主要内容,如果未能解决你的问题,请参考以下文章
linux 中修改 mysql的innodb的innodb_buffer_pool_size。
Mysql innodb_buffer_pool_size的研究
innodb_buffer_pool_size 配置原则和方式