Mysql 百问系列:Buffer Pool 是什么?

3,058 阅读6分钟

Mysql 百问系列:Buffer Pool 是什么?

问题背景知识如果没有Buffer Pool ,那么查询流程会如何?Buffer Pool 如何提高写的效率的脏页存在的价值?页的替换规则所以要淘汰哪些页面呢?结语

问题

  1. 为什么会需要 Buffer Pool ?
  2. Buffer Pool 为什么能提高读写效率?
  3. 什么是缓存命中?

背景知识

在讲解Buffer Pool 之前需要知道一些背景知识:

  1. InnoDB 类型的存储引擎是会把数据写到磁盘上的。(Mysql 5.6.6 以后默认) InnoDB 以表为单位,生成 表名.frm 和 表名.idb 文件 存储在相应的Mysql存储目录下。
  2. InnoDB 是以 页(一般为大小为16K) 为单位,从磁盘文件中读取数据 到内存的。 一页 包含多条记录(每页至少要有2条记录)。 记录有更新也是以页为单位刷新到磁盘中。每一页中的记录都是按照索引 大小顺序排序。

如果没有Buffer Pool ,那么查询流程会如何?

让我们来模拟一个简单的场景,假设有个表school , 其中有1000条数据,现在执行查询

select  * from school where id = 400

我们需要获取 id 为400的学校所有信息。

  1. 那么InnoDB 需要先找到 id =400 这条记录所在的页,把这页读取到内存,然后从这页的所有记录中找到id=400那条记录返回。
  2. 输出结果到客户端,释放内存。

查询完id =400 后,又来了一条查询语句:

select  * from school where id = 401

于是Innodb 又得按照上面流程再走一遍, 从磁盘中找到对应的页面,放到内存, 找到结果释放内存。
那么分析上面的过程,我们可以知道每次搜索都会进行磁盘IO,而磁盘IO的速度相对于CPU,内存的计算速度是远远不及的。
为了避免每一次搜索都需要进行磁盘IO, 那么InnoDB 引入了 Buffer Pool 来充当缓存``
所以查询流程就变为:

  • 先在Buffer Pool中查找 id= 400 这条记录所在的页,如果该页存在,则直接查询到记录返回,如果该页不存在,则从磁盘进行读取,放入Buffer Pool,返回记录。

(这个过程就是我们平时使用redis充当缓存的过程。先判断某个key 存不存在,如果存在则读取记录返回,如果不存在,则查询数据库,将记录存放到redis ,返回记录。)

Buffer Pool 的功能就是 缓存“页” ,减少磁盘IO,提高读写效率。

Buffer Pool 如何提高写的效率的

现在我们需要给某个学习修改名称,假设原来 id=400 的学校名称为 “aa"

update school set name = 'xs' where id = 400

更新语句的流程为:

  1. 查找 数据页 是否在Buffer Pool 中,如果存在,直接更新内存。不存在则从磁盘读取数据到Buffer Pool 中,然后更新。 (这过程其实还涉及到表是否存在非唯一索引,以及change buffer。 )
  2. 将更新内容写入redo log (什么是redo log ? 它是做什么用的?挖个坑以后填。)

从上面流程中发现一个问题没有, 我们知道Buffer Pool 是一块内存空间,我们修改了Buffer Pool 中的记录,但是并没有把记录重新写回到磁盘文件中。也就是说此时磁盘中记录的信息 和 Buffer Pool 中记录的信息是不一致的。
例子中 这个时候 磁盘中id=400 对应的记录学校名称还是"aa" ,而 Buffer Pool 中对应名称则为“xs"。

这种磁盘记录的信息与Buffer Pool 记录的信息不一致的页, 称为脏页



脏页存在的价值?

这种磁盘和Buffer Pool 记录信息不一致的情况还会有价值? 我们来想一下这种情况,比如我们需要批量更新一批数据。

update school set name = 'xs' where id > 400

这个时候会找到Buffer Pool中对应的页,然后修改。 例子中600条记录有大概率是存在在同一页面中的。上面提到过页中的记录是按索引排序的,而且一般16K大小的页,能存放的记录数是几百上千条。
由于不是每一条记录更改都会更新到磁盘,所以上面的更新语句大大节省了时间,毕竟写数据到磁盘是十分耗时的。

当然脏页也不可能一直存在在Buffer Pool 中,总要将信息刷新到磁盘中。不然如果程序出问题,或者机器断电,更新的信息不就丢失了嘛。
那么什么时候会把Buffer Pool 中的信息更新到磁盘中呢?

  • Mysql 会有专门的后台线程,每隔一段时间就将脏页 刷新到磁盘
  • Buffer Pool 空间不足, 脏页将被淘汰 从 Buffer Pool 中移除的时候
  • redo log 写满的时候
  • 数据库正常关闭的时候。

页的替换规则

Buffer Pool 是一片内存空间,受制于内存空间大小。 可以通过innodb_buffer_pool_size 来控制Buffer Pool 的大小。所以Buffer Pool 内存放的页 数量也是有限的。假设Buffer Pool 中只能存放1000个页,并且已经存满。 那么如果下一次查询的记录并不在这1000页中,需要从磁盘读取相应页 加载到Buffer Pool 中。由于Buffer Pool 已经满了,所以必须要把其中某一个页面先移除, 才能把新的页面放进来。

所以要淘汰哪些页面呢?

我们知道如果查询或者修改记录,记录的页已经存在在Buffer Pool 中那么将有效的提高效率。最理想的情况是每次访问页面都已经存在在Buffer Pool 中,我们称为 缓存命中。淘汰页面的算法,对 缓存命中率 至关重要。
由于Buffer Pool的缓存淘汰规则比较复杂,后面另开一篇进行讲解。核心思想就是把最近最少使用的页面淘汰,也就是 LRU (Least recently used)算法。

结语

  • Buffer Pool 是一块内存空间。 将页面加载到Buffer Pool 中可以减少磁盘IO 提高查询效率。
  • 每次更新先更新Buffer Pool 中的记录,并将存放该记录的页标记为 脏页。后台进程每隔一段时间刷新脏页到磁盘提高磁盘IO效率。Buffer Pool的引入提升了读写效率。
  • 缓存命中率 体现了Buffer Pool的利用效率。Buffer Pool 通过LRU算法来淘汰页面提高缓存命中率。

喜欢和支持,请关注下个人公众号,谢谢。