聊一聊MySQL的Buffer Pool

数据库 MySQL
咱们在使用mysql的时候,比如很简单的select * from table;这条语句,具体查询数据其实是在存储引擎中实现的,大家都知道mysql数据其实是放在磁盘里面的,如果每次查询都直接从磁盘里面查询,这样势必会很影响性能,所以一定是先把数据从磁盘中取出,然后放在内存中,下次查询直接从内存中来取。

[[384326]]

本文转载自微信公众号「程序员小饭」,作者饭米粒 。转载本文请联系程序员小饭公众号。饭米粒  

前言

buffer pool是什么

咱们在使用mysql的时候,比如很简单的select * from table;这条语句,具体查询数据其实是在存储引擎中实现的,大家都知道mysql数据其实是放在磁盘里面的,如果每次查询都直接从磁盘里面查询,这样势必会很影响性能,所以一定是先把数据从磁盘中取出,然后放在内存中,下次查询直接从内存中来取。但是一台机器中往往不是只有mysql一个进程在运行的,很多个进程都需要使用内存,所以mysql中会有一个专门的区域来处理这些数据,这个专门为mysql准备的区域,就叫buffer pool。

buffer pool的工作流程

咱们以查询语句为例 1:在查询的时候会先去buffer pool(内存)中看看有没有对应的数据页,如果有的话直接返回 2:如果buffer pool中没有对应的数据页,则会去磁盘中查找,磁盘中如果找到了对应的数据,则会把该页的数据直接copy一份到buffer pool中返回给客户端 3:下次有同样的查询进来直接查找buffer pool找到对应的数据返回即可。

大家看到这里相信应该对buffer pool有了个大概的认识,有没有感觉有点缓存的感觉,当然buffer pool可没有缓存那么简单,内部结构还是比较复杂的,不过没关系,咱们继续往下看。

buffer pool数据管理

数据管理的基本单位

buffer pool毕竟是一种内存管理,数据当然不是按照一条一条的sql语句来管理的,而是按照数据页来管理的,innodb 引擎默认的数据页是16kb,而buffer pool启动的时候是默认的128M,所以是有8192个数据页的。而磁盘的数据管理也是用数据页为单位来管理的,所以每次查找数据的时候,先请求buffer pool,buffer pool中没有的话会到磁盘中找到对应的数据页,然后copy到buffer pool中给客户端返回。

free链表

正常情况下,buffer pool肯定是从第一个数据页,不断的往后填充的,一个一个的往后写入,每次直接在后面追加就可以了。如下图(黄色部分表示已经写入数据)

但是实际生产环境中,并不是这样的,我们不光有查询操作,还有删除,修改等操作,而且已经写入buffer pool的数据不一定是始终有价值的,有一些数据是不需要的,需要释放对应的数据页的,所以就会造成buffer pool的数据其实是这种情况,间断不连续的。

在这种情况下该如何去找到有效的空闲的数据页空间来存储数据呢?最直观的方法就是从第一个页遍历的一个一个的往后找,找到空闲的数据页即可,这种方法倒是可行,但是非常影响效率,所以mysql在处理这种问题用上了free链表的方式来管理空闲的数据页。

大家可以看一看free链表的结构

  • free链表有一个基节点,记录了该free链表的唯一标志,该链表的尾节点地址,以及链表的总长度
  • 基节点后面会有很多的控制块,控制块本身很小,只是存储了指向空闲数据页的指针而已,所以buffer pool在寻找空闲数据页的时候直接用free链表可以直接找到。
  • 只要有一页数据空闲出来之后,直接把该数据页的地址追加到free链表即可。

flush链表

当然只是用free链表是解决不了所有问题的,比如:我们在执行update table test set field_a = 1;的时候,我们是先修改buffer pool里面对应的数据页,然后再更新磁盘中对应的数据页的,(当然这里会涉及到一个数据一致性的问题,mysql是用redo log解决的,这个不在咱们这篇文章的讨论范围之内)我们把buffer pool中对应修改的数据页同步修改到磁盘的时候,这个过程称之为"刷脏",刷脏是有一定策略的,可以用

  1. select @@innodb_flush_log_at_trx_commit; 

来查看刷脏策略

我们一般都不会设置实时写,这样很影响性能,所以一般都是延迟写的,那么就会引发一个问题,mysql是如何在buffer pool中找到被修改过的脏数据的呢?这里咱们就用上了flush链表了,其实和free链表比较像

flush链表上面维护的都是脏数据页的指针。刷脏的时候直接遍历flush链表去刷脏就可以了。

lru链表

buffer pool是有一定空间限制的,默认是128M,总会有空间塞满的时候的,所以数据页是有淘汰机制的,淘汰机制就是lru(最近最少使用)。

lru原理其实也很简单,使用到过的数据页,直接移动到链表的头部,然后在buffer pool满了之后直接淘汰掉链表尾部的数据页就可以了。

lru链表的优化

其实简单的lru链表是存在一定的问题的,比如咱们在工作过程中,可能会用上 select * from test这样的语句来进行一些刷数据等需求,如果test表是非常大的,很有可能一下子把buffer pool占满,把之前的数据页全部都淘汰掉,然后其余的数据在线上业务正常执行的时候,又会回来重新把之前select * from test 占用的数据页重新慢慢淘汰掉,这一来一去是非常影响线上的性能的。

所以鉴于以上所在的问题,mysql的buffer pool是在lru的基础上进行了一些优化的。

buffer pool的lru链表把数据分为了热数据块和冷数据块,比例大概5:3的样子,每次新的数据页写入都会写入冷数据区。

 

但是如果这样的话那么热数据区永远都不会有数据,所以冷数据区写入的时候会另外记录上写入的时间,下次访问该数据区的时候如果时间间隔大于1s,那么就会放入热数据区,这样就不会淘汰掉大量的无辜数据。所以我们在执行select * from test这种语句刷新脚本的时候,只会占用冷数据的空间,而不会影响到热数据。

 

责任编辑:武晓燕 来源: 程序员小饭
相关推荐

2020-10-15 06:56:51

MySQL排序

2021-04-23 10:31:18

MySQLRole数据库

2022-12-26 08:13:54

子查询MySQL

2021-07-30 10:33:57

MySQL触发器数据

2023-07-06 13:56:14

微软Skype

2023-09-22 17:36:37

2021-01-28 22:31:33

分组密码算法

2020-05-22 08:16:07

PONGPONXG-PON

2020-09-08 06:54:29

Java Gradle语言

2021-08-01 09:55:57

Netty时间轮中间件

2023-09-27 16:39:38

2022-02-21 15:01:45

MySQL共享锁独占锁

2018-06-07 13:17:12

契约测试单元测试API测试

2022-08-08 08:25:21

Javajar 文件

2023-05-15 08:38:58

模板方法模式

2021-08-04 09:32:05

Typescript 技巧Partial

2021-01-29 08:32:21

数据结构数组

2021-02-06 08:34:49

函数memoize文档

2019-02-13 14:15:59

Linux版本Fedora

2018-11-29 09:13:47

CPU中断控制器
点赞
收藏

51CTO技术栈公众号