正文
今天填一下之前的坑,盘一盘 mysql 相关的 buffer。
我们来看一下官网的一张图:
这张图画的是 mysql innodb 的架构,从图中可以看到有很多 buffer,这篇我们就一个一个盘过去。
发车!(文档基于mysql8.0,以下描述的存储引擎基于 mysql innodb)
首先,我们来看下 buffer pool。
其实 buffer pool 就是内存中的一块缓冲池,用来缓存表和索引的数据。
我们都知道 mysql 的数据最终是存储在磁盘上的,但是如果读存数据都直接跟磁盘打交道的话,这速度就有点慢了。
所以 innodb 自己维护了一个 buffer pool,在读取数据的时候,会把数据加载到缓冲池中,这样下次再获取就不需要从磁盘读了,直接访问内存中的 buffer pool 即可。
包括修改也是一样,直接修改内存中的数据,然后到一定时机才会将这些脏数据刷到磁盘上。
看到这肯定有小伙伴有疑惑:直接就在内存中修改数据,假设服务器突然宕机了,这个修改不就丢了?
别怕,有个 redolog 的存在,它会持久化这些修改,恢复时可以读取 redolog 来还原数据,这个我们后面的文章再详盘,今天的主角是 buffer 哈。
回到 buffer pool,其实缓冲池维护的是页数据,也就是说,即使你只想从磁盘中获取一条数据,但是 innodb 也会加载一页的数据到缓冲池中,一页默认是 16k。
当然,缓冲池的大小是有限的。按照 mysql 官网所说,在专用服务器上,通常会分配给缓冲池高达 80% 的物理内存,不管分配多少,反正内存大小正常来说肯定不会比磁盘大。
也就是说内存放不下全部的数据库数据,那说明缓冲池需要有淘汰机制,淘汰那些不常被访问的数据页。
按照这个需求,我们很容易想到 LRU 机制,最近最少使用的页面将被淘汰,即维护一个链表,被访问的页面移动到头部,新加的页面也加到头部,同时根据内存使用情况淘汰尾部的页面。
通过这样一个机制来维持内存且尽量让最近访问的数据留在内存中。
看起来这个想法不错,但 innodb 的实现并不是朴素的 LRU,而是一种变型的 LRU。
从图中我们可以看出 buffer pool 分为了老年代(old sublist)和新生代(new sublist)。
老年代默认占 3/8,当然,可以通过 innodb_old_blocks_pct 参数来调整比例。
当有新页面加入 buffer pool 时,插入的位置是老年代的头部,同时新页面在 1s 内再次被访问的话,不会移到新生代,等 1s 后,如果该页面再次被访问才会被移动到新生代。