MySQL缓冲池Buffer Pool

Buffer Pool作用

MySQL数据是存储在磁盘中,但是如果每次都从磁盘读取数据,性能极差。要想提高查询性能,就需要在内存中增加一块缓存区间,将磁盘中读取到的数据缓存在内存,直接读取,为此InnoDB存储引擎设计了缓冲池(Buffer pool)
MySQL缓冲池Buffer Pool_第1张图片

  • 读取数据时,如果数据在Buffer pool中,直接从Buffer pool中读取数据,否则从磁盘中读取数据。
  • 修改数据时,先将修改后数据放入Buffer pool中,并将其设置为脏页,由后台线程看向更新入磁盘中。

buffer pool大小一般为默认128MB。磁盘和内存交互的基本单位是(16KB)。MySQL启动时申请一篇连续空间,按照16KB为基本单位划分出一个个页。缓存除了有索引页、数据页还有undo页、锁信息等。因此从磁盘读入到内存的最小单位是
MySQL缓冲池Buffer Pool_第2张图片
为了方便管理缓冲池,在Buffer pool的最前面为每个缓存页都会有一个控制块来记录缓存页的表空间、页号、地址等信息。
MySQL缓冲池Buffer Pool_第3张图片

如何管理Buffer Pool

启动MySQL服务器时,对缓冲池进行初始化,将空间分为控制块和缓存页。
如何区分空闲空间?使用链表结构,将空闲缓存页的控制块作为为链表的节点,串联起来形成Free链表。
MySQL缓冲池Buffer Pool_第4张图片

如何管理脏页?

为了提高数据库的写功能,更新插入数据时,先将数据缓存在Buffer pool中,并将Buffer Pool中对应的页称为脏页,由后台线程择机写入到磁盘。为了快速定位那些是脏页,设计了类似出Flush链表将脏页集中管理。
MySQL缓冲池Buffer Pool_第5张图片

如何提高Buffer Pool的性能?

首先缓冲区的大小是有限的,对于大量的数据不可能将所有的数据都缓存到Buffer Pool,所以我们希望在提高缓冲池里面数据的命中率,尽量存储经常访问的数据,很少访问的数据淘汰掉,实现这个,最常用的就是LRU(Least recently used)算法。
LRU算法思想是保证链表头部节点是最近使用过的数据,尾部放的数据最久没被使用,当从没有命中缓存,从磁盘中读取到新的数据放入链表头部,并淘汰链表尾部数据腾出空间。

基本的LRU算法并没有在MySQL中使用,因为基本的LRU算法无法规避下面两个问题:

  • 预读失效;
  • 缓存污染;

什么是预读失效?

MySQL的预读机制。当从磁盘中访问数据附近的数据,在未来有很大的概率也会被访问到,所以MySQL在加载数据时会把相邻的数据也会加载进来,这样可以很大概率的减少磁盘I/O。如果这些提前加载进来的数据没有被访问,这个预读操作相当于白做了,这就叫“预读失效”。
使用简单的LRU算法会把预读的数据页加载放到链表头部,Buffer pool空间不足时还需要把原来末尾的数据淘汰掉。这些预读数据如果一直没有访问,而且还占用缓冲空间,就会大大降低缓存命中率。

如何才能避免预读失效呢?
按照经验来说这些预读进来的数据还是有很大概率被访问到,所以该预读还是得预读,但是需要降低其在Buffer pool区的停留时间,让真正访问到的数据页放到链表头部(给机会还是自己要中用),保证真正的热点数据不会被淘汰数去。所以MySQL是这样设计的:
将LRU链表划分为young区old区,把预读进来数据页先放进old区,后面真正被访问到时才将其挪入到young区链表头部,将young区数据页淘汰的放入old区头部。预读的数据后面如果一直没有被访问,优先淘汰old区的数据页,因此不会影响young区的热点数据。
MySQL缓冲池Buffer Pool_第6张图片
预读了数据页20,原来old区尾部的10被淘汰掉:
MySQL缓冲池Buffer Pool_第7张图片
然后预读页20被访问了,放入young区头部,有young区原来的7淘汰放入old区头部,old依次后移:
MySQL缓冲池Buffer Pool_第8张图片

如何避免缓存失效?

当SQL语句扫描了大量的数据,在Buffer pool空间有限的情况下,则会可能吧原来存放的热点数据都替换出去,后面热点数据再次被访问是,命中率就会非常低了,因此得重新从磁盘中读取,产生大量磁盘IO,降低性能,这个过程称为缓存污染
例如查找一条数据没有走索引:

select * from user where name = '%jie%';

上面语句会导致索引失效,因此走的是全表扫描。、
因此我们需要提高数据从old区进入young区的门槛,保证young区的数据不会轻易被替换掉MySQL是这样设计的:

  • 加载到old区的数据第一次访问时间与后续访问时间间隔过小的话,缓存页就不会被从old区加载到young区,反之,超过设定的最小时间间隔,将该缓存页移动到缓存页的头部。
    时间间隔由参数innodb_old_blocks_time控制,默认1000ms。

Buffer pool中的脏页什么时候刷入磁盘?

为了提高数据库系统的性能,不能每次修改后都将buffer pool中脏页更新到磁盘,而是先将修改记录保存到redo log中(Write Ahead Log策略,写入日志文件是顺序写入没更新脏页到磁盘是随机写)。
以下几种情况会触发脏页的刷新:

  • redo log 日志文件满了
  • Buffer Pool空间不足,需要淘汰部分数据,如果是脏页,要先将脏页同步到磁盘
  • MySQL空闲,后台线程定期更新适量的脏页数据
  • MySQL正常关闭前,把所有脏页数据刷入磁盘

当开启了慢SQL监控后,欧文会友稍长时间的额SQL,可能是脏页刷新到磁盘导致。短时间内频繁出现,可考虑适当的调大Buffer Pool空间或者redo log日志大小,减少脏页刷盘率。

以上图片和内容参考答应我,这次要搞懂 Buffer Pool

就是这事,散会。

你可能感兴趣的:(MySQL,mysql,数据库,缓冲池,预读失效)