一、什么是缓冲池
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表示查询的是非系统表。
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
三、 设置缓冲池 建议设置60%~70%
在my.cnf中设置 innodb_buffer_pool_size = 512M 即可
查看
innodb_buffer_pool_instances 缓冲池的实例个数 这个变量建议修改为CPU的核数。
将缓冲池划分为用户指定数量的单独区域,每个区域都有自己的LRU列表和相关数据结构,以减少并发内存读取和写入操作期间的争用,增加并发的性能。减少锁的颗粒度。
四、查看innodb buffer的使用情况
命令:SHOW ENGINE INNODB STATUS;
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 如下图
B) 当从硬盘读取一条记录的时候, Free List会移动一个页到CRU List中
这里存在一个问题,当mysql同时查询两条不同页的数据,这里是不是会并发的从Free List申请空闲页。这个时候就需要innodb_buffer_pool_instances来解决并发,因为有多个buffer pool实例的话就不会产生并发。
六 、 LRU List 的管理
LRU List里面都是存储的最热的数据。
普通的CRU算法是,读到的页就立马放入到CRU List的最前面。
Mysql中采用了 midpoint LRU算法。
当我们有一个最新的页需要进入到LRU链表时,并不是直接放到最前面,而是放到LRU List的3/8的位置,也就是插入到下面的midpoint的位置。如下图所示:
new表示的是最热的数据,也就是经常访问的数据, old表示不经常访问的数据。
① midpoint的位置可以通过innodb_old_blocks_pct变量来修改,下图的37是37%的意思。如果设置50%则是表示new old各一半。
② 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以前的做法是
查询一张比较热的表,进行预热
② 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的文件里面
上面的load的预热数据,不是关闭mysql前buffer中的全部数据,而是通过下面的参数配置的,默认是25%,建议改成40%。
innodb_buffer_pool_dump_pct ## 预热的数据百分百
建议看一下 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
感谢上面的参考的大神