mysql5.7 Buffer Pool特性介绍。innodb三大特性:双写缓冲区、Buffer Pool、AHI(自适应HASH索引)
本章主要介绍Buffer Pool特性。
双写缓冲区参考《mysql5.7系统表空间和独立表空间,断,组,区,页的概念,innodb双写缓冲区》
AHI(自适应HASH索引)放的都是很热的数据,是buffer pool中的一部分。
即使select一行数据,innodb也会读取一页16kb数据,那么当一行数据返回到应用层时,innodb分析出来有很大的概率数据都是顺序读的,比如分页,那么innodb此时会把这一页缓存起来,称为Buffer Pool,这个buffer默认是128MB:
show variables like 'innodb_buffer_pool_size'; -- 默认134217728字节 = 128MB
可以在mysql的配置文件里把它调大,默认128MB是偏小的。
Buffer Pool里面肯定放的是缓存页,这个页和磁盘大小是一样的,都是16KB,里面会有很多页,一个页就有一个控制块,控制块会存很多信息:表空间编号、页号、页地址等等控制信息,一个控制块就对应了一个缓存页,控制块在buffer pool中的位置:
一个控制块大约占页的5%大小,也就是 16kb * 5%,上面提到的innodb_buffer_pool_size里的大小是不包含控制块大小的。如果你设置的是128MB,那么系统申请的将是 128 * 105% 会把控制块大小再加上去。
通过控制块是可以找到唯一缓存页的,把所有控制块串联起来组成一个free链表,这是一个双向链表,这里面记录这当前这个buffer pool里的所有空闲页面。
无论是update还是select,页数据都会加载到buffer pool里面。buffer pool里的页在commit提交的时候写入数据。
【free双向链表】
所以当我们要用到buffer pool时,先到free链表里使用表空间id+页id的hash值来快速判断某一个页是否存在,取完之后,从free链表里移除,当页数据读到buffer pool时,除了返回给select语句的结果集,还会有大量的数据因为读页的关系被冗余过来,又因为mysql会判断读出来的页数据很有可能会被应用再次顺序读取,所以mysql不会马上清除,会继续被update等操作写入页从而变成脏页。
【flush双向链表】
脏页会被记录到flush双向链表里,flush双向链表的结构基本和free双向链表的结构一致,flush双向链表里的页表示之后会被刷新到磁盘里。
【LRU链表】
buffer pool的内存大小一定比不上磁盘大小,如果free链表里没有空闲页,这时候需要把页从buffer pool中释放出去,那这个释放规则是怎样的呢?就是每次当buffer pool中的页有更新时,就会把该页放入LRU链表的头部,这样LRU链表的尾部页就是更新时间比较久远的页,可以最先释放。
双写缓冲区(双写机制)的区是放在系统表空间的,是为了mysql写页面的数据完整性而创建的。双写一次写一个区,总共两个区。双写缓冲区是Buffer Pool中的一部分:
【LRU链表优化】
LRU链表是最近最优使用链表
1)如果某一次全表扫描的查询数据量非常大,直接占满整个buffer pool,那么buffer pool不会清空之前的页去全力缓存这些页,而是像JVM一样分新生代和老年代一分为二LRU链表,这些全表扫描的但是可能使用频率很小的放到老年代中,然后老年代和新生代中淘汰时间按照格子的更新时间最慢的来淘汰就好。
2)如果两次访问某页的时间非常近在一秒以内,那么mysql会认为这个select可能是个全表扫描操作,就不会按照第二次的读取时间置顶到LRU链表首部,因为mysql认为下一次再这么全表扫描的概率不会太大,不需要置顶。
3)但是如果被访问的区域在样区域的后四分之一,那么mysql才会考虑置顶。
【什么时候刷新脏页到磁盘?】
1)BUF_FLUSH_LRU 扫描LRU链表,按照最远使用页来刷新,刷新一部分页到磁盘,当buffer pool中不够新的存储空间时,但是如果没有更新过的页就直接释放就可以。
2)BUF_FLUSH_LIST 扫描FLUSH链表,刷新一部分页到磁盘
3)BUF_FLUSH_SINGLE_PAGE 单页刷新
【buffer pool的个数】
当所有读写都涉及到buffer pool时,就涉及到多线程操作的线程安全问题,innodb_buffer_pool_chunk_size这个参数大于1GB的时候,mysql才会允许设置多个buffer pool,否则只能是一个buffer pool。如果调大buffer pool,mysql server需要向操作系统申请新的内存大小,所以更改后只能重启,建议第一次启动之前就设置合理参数。在mysql5.75版本之后,mysql对于buffer pool的内存申请进行了优化,对于每个buffer pool chunk进行申请,而不是全部的buffer pool, 所以在mysql运行期间也可以修改了,旨在把buffer pool以chunk为单位申请一块一块的内存。 show engine innodb status\G 查看buffer pool状态
end.