buffer pool是什么?
- 是一块内存区域,当数据库操作数据的时候,把硬盘上的数据加载到buffer pool,不直接和硬盘打交道,操作的是buffer pool里面的数据
- 数据库的增删改查都是在buffer pool上进行,和undo log/redo log/redo log buffer/binlog一起使用,后续会把数据刷到硬盘上
- 默认大小 128M
数据页
- 磁盘文件被分成很多数据页,一个数据页里面有很多行数据
- 一个数据页默认大小 16K
- 更新一行数据,实际上是把行数据所在的
数据页
整个加载到buffer pool中
缓存页
- buffer pool中存放的数据页我们叫缓存页,和磁盘上的数据页是一一对应的,都是16KB
- 缓存页的数据,是从磁盘上加载到buffer pool当中的
缓存页描述信息(描述信息块)
- 存的是 数据页所属的表空间号,数据页编号,数据页地址等信息
- 放在缓存页的前面
- 每个描述信息块大小是缓存页的5%左右,大约是 1610240.05=800个字节
buffer pool初始化
1 数据库只要一启动,就会按照你设置的Buffer Pool大小,稍微再加大一点,去找操作系统申请一块内存区域,作为Buffer Pool的内存区域
2 然后当内存区域申请完毕之后,数据库就会按照默认的缓存页的16KB的大小以及对应的800个字节左右的描述数据的大小,在Buffer Pool中划分出来一个一个的缓存页和一个一个的他们对应的描述数据
free链
- 作用:帮助我们找到空闲的缓存页
- 是一个双向链表,链表节点是空闲的缓存页对应的描述信息块(空的缓存页)
- 链表上除了描述信息块,还有一个基础节点,存储了free链有多少个描述信息块,也就是有多少个空闲的缓存页
- 当我们加载数据的时候,会从free链中找到空闲的缓存页,把数据页的表空间号和数据页号写入描述信息块;加载数据到缓存页后,会把缓存页对应的描述信息块从free链表中移除
怎么知道数据页是否被缓存?
- 数据库中有一个 数据页缓存哈希表,用表空间号+数据页号,作为一个key,然后缓存页的地址作为value
- 表空间号+数据页号 = 缓存页地址
什么是脏缓存页?
- 被更新过的缓存页,数据和磁盘上的数据不一致,所以是脏缓存页
- 脏缓存页的数据是要刷到磁盘上的
flush链表
- 是一个双向链表,链表结点是被修改过的缓存页的描述信息块(更新过的缓存页)
- 作用:帮我们找到脏缓存页,也就是需要刷盘的缓存页
- 和free链表一样,也有一个基础结点,链接首尾结点,并存储了有多少个描述信息块
- 最后要把flush链表上结点对应的缓存页刷盘,后台线程会在MySQL不怎么繁忙的时候,找个时间把flush链表中的缓存页都刷入磁盘中,这样被你修改过的数据,迟早都会刷入磁盘的;缓存页从flush链表中移除,加入到free链表当中
LRU链表
- 是一个双向链表,链表结点是 非空的缓存页对应的描述信息块(有数据的缓存页,包含更新过和未更新过的缓存页,范围比flush链表大,flush链表是它的子集)
- 作用:用来淘汰不常被访问的缓存页
- LRU链表分为热数据区和冷数据区,冷数据区占了总链表的37%
- 冷数据区是不常访问的缓存页
- 热数据区是经常访问的缓存页
- 加载数据的时候,缓存页会放在冷数据区的头部
- 数据页加载到缓存页后,在1s之后,访问该缓存页,该缓存页会被移动到热数据区头部
- 数据页刚加载到缓存页后,在1s之内,访问该缓存页,该缓存页是不会被移动到热数据区头部的
- 什么时候会lru中的缓存页刷盘并清空?
- 当缓存页用完的时候,把冷数据区尾部的缓存页刷盘清空,缓存页对应的信息描述块从lru链表中移除,加入到free链表当中
- 有一个后台线程,他会运行一个定时任务,这个定时任务每隔一段时间就会把LRU链表的冷数据区域的尾部的一些缓存页,刷入磁盘里去,清空这几个缓存页,把他们加入回free链表去;如果该缓存页也在flush链表中(该缓存页更新过),也需要把该缓存页从flush链表中移除
- 当缓存页用完的时候,把冷数据区尾部的缓存页刷盘清空,缓存页对应的信息描述块从lru链表中移除,加入到free链表当中
- 热数据区的前1/4的缓存页如果被访问,是不会移动到热数据区头部的;后3/4的缓存页被访问了,才会移动到热数据区头部
预读机制
- 所谓预读机制,说的就是当你从磁盘上加载一个数据页的时候,他可能会连带着把这个数据页相邻的其他数据页,也加载到缓存里去
- 什么时候会触发预读机制?
- 有一个参数是innodb_read_ahead_threshold,他的默认值是56,意思就是如果顺序的访问了一个区里的多个数据页,访问的数据页的数量超过了这个阈值,此时就会触发预读机制,把下一个相邻区中的所有数据页都加载到缓存里去
- 如果Buffer Pool里缓存了一个区里的13个连续的数据页,而且这些数据页都是比较频繁会被访问的,此时就会直接触发预读机制,把这个区里的其他的数据页都加载到缓存里去
- 全表扫描的时候,select * from tableName 会把该表所有的数据页都缓存到buffer pool当中
Buffer Pool的缓存页以及几个链表的使用回顾
- 数据库启动时,会申请内存创建buffer pool,buffer pool分成一个个缓存页及其缓存页描述信息块,描述信息块加入到free链表中
- 数据加载到一个缓存页,free链表里会移除这个缓存页,然后lru链表的冷数据区域的头部会放入这个缓存页
- 如果查询了一个缓存页,那么此时就会把这个缓存页在lru链表中移动到热数据区域去,或者在热数据区域中也有可能会移动到头部去
- 如果更新了缓存页,会把该缓存页加入到flush链表中
- 如果缓存页不够用了,会把lru冷数据区尾部的缓存页刷盘,清空;该缓存页从lru链表和flush链表中移除,加入到free链表中
- mysql后台线程也会定时把lru冷数据区尾部的缓存页刷盘,清空;定时把flush链表中的缓存页刷盘,清空,加入到free链表中
- 总结
- 一边不停的加载数据到缓存页里去,不停的查询和修改缓存数据,然后free链表中的缓存页不停的在减少,flush链表中的缓存页不停的在增加,lru链表中的缓存页不停的在增加和移动
- 另外一边,你的后台线程不停的在把lru链表的冷数据区域的缓存页以及flush链表的缓存页,刷入磁盘中来清空缓存页,然后flush链表和lru链表中的缓存页在减少,free链表中的缓存页在增加
文章和图片参考救火队长mysql