Mysql InnoDB 缓冲池

一、什么是缓冲池

InnoDB存储引擎的数据都是每行存储在页里面的。当需要查询的时候,直接访问磁盘会很慢,为了提高IO速度,放访问某个页上面的数据时,把页的数据缓存到内存里面,下次查询的时候,不需要从磁盘IO操作,直接从内存读取,从而提高了效率。这个缓存内存就是缓冲池。

二、缓冲池存放了那些内容

① 数据页

② 索引页

③ change buffer

④ 自适应哈希

⑤ 锁

三、 缓冲池的机制是怎么样的?

我们有一个test表,现在需要查询id = 10的这条记录。假设id = 10的记录存放到 A 这个页里面。

① 第一次查询id = 10, mysql会找到A页,在从A页里面通过二分法找到id = 10的记录。这时,mysql就会把A页进行缓冲起来。放到buffer pool里面。注意这里缓冲的不是id = 10的记录,而是A页。

② 当第二次查询id = 10; 我们怎么找到这个A页? 

首选要介绍一下 space和page_number, space指的是每张表的一个标识,名称叫表空间id,

可以在 information_schema数据库中 select * from innodb_sys_tablespaces where space > 25 进行查看,大于25表示查询的是非系统表。

Mysql InnoDB 缓冲池_第1张图片

 page_number 指的是页的偏移量,相当于是我们的页存在ibd文件中,属于第几个页,

在innodb buffer中存在个hash表,存放的就是以 space为键,值为page_number的hash表结构,当我们查询id = 10时, 根据索引查出id存在A页里面,然后就可以通过space找到对应的内存的test表的页,再根据页的page_number 偏移量找到对应的A页。

我们可以通过 innodb_buffer_page表找到每个space对应的page_number

select space,page_number,page_type from innodb_buffer_page where space > 25 limit 10;

注意:线上不建议执行这条sql

Mysql InnoDB 缓冲池_第2张图片

三、 设置缓冲池 建议设置60%~70%

在my.cnf中设置 innodb_buffer_pool_size = 512M 即可

查看

Mysql InnoDB 缓冲池_第3张图片

innodb_buffer_pool_instances 缓冲池的实例个数 这个变量建议修改为CPU的核数。

将缓冲池划分为用户指定数量的单独区域,每个区域都有自己的LRU列表和相关数据结构,以减少并发内存读取和写入操作期间的争用,增加并发的性能。减少锁的颗粒度。

四、查看innodb buffer的使用情况

命令:SHOW ENGINE INNODB STATUS;

Mysql InnoDB 缓冲池_第4张图片

 Total memory allocated :        分配给缓冲池的总内存 这里的单位是字节 

Dictionary memory allocated    : 分配给数据字典中的内存单位是字节 

 Total memory allocated:是mysql实际使用的内存大小,这个是大于buffer pool size的

Buffer pool size: 分配给缓冲池的页面的总大小, 这里指的是页的大小,图上面的32768是32768页, 一个页是16KB 则缓冲池大小 = 32768 * 16 / 1024 = 512M

Free buffers : 缓冲池空闲列表的总页大小,就是Free List ,单位:页

Database pages: buffer pool LRU列表的总页面大小。 单位:页

Old database pages:old LRU子列表的总页面大小。单位:页

Modified db pages: 表示就是脏页的数量 单位:页

五、InnoDB buffer pool 缓冲池的组成

① Free List  空闲的链表list

② LRU List  已经存放数据(已经使用的页)的链表list

③ Flush List 脏页list ,Flush 页其实是CRU List 的指针,LRU List包括Flush List

buffer pool是一个页一个页进行管理的,LRU List、Free List、Flush List 则是通过链表连接这些页的。buffer pool大小 =  Free List  大小 + LRU List 大小Flush List存储是CRU List的指针。

可以通过SHOW ENGINE INNODB STATUS;查看每个list的大小。 

④ 这个脏页是什么意思了? 当我们读到一条数据,同时执行update操作时,内存的数据和硬盘的数据不一致的时候, 这个页就叫做脏页。

⑤ 硬盘的页到buffer pool的流程是怎么样的?

A)mysql 启动的时候,假如内存Free List 如下图

Mysql InnoDB 缓冲池_第5张图片

 B) 当从硬盘读取一条记录的时候, Free List会移动一个页到CRU List中

这里存在一个问题,当mysql同时查询两条不同页的数据,这里是不是会并发的从Free List申请空闲页。这个时候就需要innodb_buffer_pool_instances来解决并发,因为有多个buffer pool实例的话就不会产生并发。

Mysql InnoDB 缓冲池_第6张图片

六 、 LRU List 的管理

LRU List里面都是存储的最热的数据。

普通的CRU算法是,读到的页就立马放入到CRU List的最前面。

Mysql中采用了 midpoint LRU算法。

当我们有一个最新的页需要进入到LRU链表时,并不是直接放到最前面,而是放到LRU List的3/8的位置,也就是插入到下面的midpoint的位置。如下图所示:

Mysql InnoDB 缓冲池_第7张图片

new表示的是最热的数据,也就是经常访问的数据, old表示不经常访问的数据。

 ① midpoint的位置可以通过innodb_old_blocks_pct变量来修改,下图的37是37%的意思。如果设置50%则是表示new old各一半。

Mysql InnoDB 缓冲池_第8张图片

 ② innodb_old_blocks_time 如果是1000,表示1S,意思就是1S中,如果数据在1S中被读取了两次则把当前页放到LRU最前面。

如果这个值是0,第一次被插入到midpoint位置,如果第二次查询,数据页还在LRU List中,则就会把该数据页放到LRU List的最前面。同理innodb_old_blocks_time=1000.则是1S中被查询,才被放到LRU List的最前面。

七、 预热

当我们重启mysql时,如果buffer pool比较大的话,例如有300G的内存,如果一开始数据请求300G,那么磁盘肯定会读写很慢,所以我们需要先把buffer pool进行预热,提前把热的数据放入到buffer pool中。

① 5.6以前的做法是

查询一张比较热的表,进行预热

Mysql InnoDB 缓冲池_第9张图片

② 5.6后mysql就处理了这个问题

innodb_buffer_pool_dump_at_shutdown 当mysql关闭时,会自动将buffer pool中的热数据进行保存

innodb_buffer_pool_load_at_startup 当mysql启动时,就会把上次的buffer pool热数据进行装载

上面dump和load的文件保存的是space,page number, 表空间id和页的偏移量

文件存放到 数据目录/ib_buffer_pool的文件里面 

Mysql InnoDB 缓冲池_第10张图片

上面的load的预热数据,不是关闭mysql前buffer中的全部数据,而是通过下面的参数配置的,默认是25%,建议改成40%。

innodb_buffer_pool_dump_pct ## 预热的数据百分百

Mysql InnoDB 缓冲池_第11张图片

建议看一下 https://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool.html

其他的备注信息

buffer pool两个最主要的功能:一个是加速读,一个是加速写。加速读呢? 就是当需要访问一个数据页面的时候,如果这个页面已经在缓存池中,那么就不再需要访问磁盘,直接从缓冲池中就能获取这个页面的内容。加速写呢?就是当需要修改一个页面的时候,先将这个页面在缓冲池中进行修改,记下相关的重做日志,这个页面的修改就算已经完成了。至于这个被修改的页面什么时候真正刷新到磁盘,这个是buffer pool后台刷新线程来完成的。

参考mysql-缓冲池_jaylaozhou的博客-CSDN博客_mysql 缓冲池

https://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool.html

感谢上面的参考的大神

你可能感兴趣的:(mysql,缓存,数据库)