看一篇文章了解InnoDB缓冲池(bufferpool)的工作原理以表空间的形式存储,所谓表空间只是InnoDB对文件系统上的一个或几个实际文件的抽象,以及毕竟实际数据仍然存储在磁盘上。磁盘的速度很慢,怎么比得上“快如闪电”的CPU呢?InnoDB存储引擎在处理客户端的请求时,当需要访问某个页面的数据时,会将整个页面的所有数据加载到内存中。也就是说,即使我们只需要访问一个页面的一条记录,也需要先将整个页面的数据加载到内存中。缓冲池的内部组件缓冲池中默认的缓存页面大小与磁盘上的默认页面大小相同,一般为16KB。为了更好地管理缓冲池中的这些缓存页,InnoDB为每个缓存页创建了一些所谓的控制信息。这些控制信息包括页面所属表空间的编号、页号、缓存页在缓冲池中的地址、链表的节点信息以及一些锁信息。缓冲池的一些参数:SHOWVARIABLESLIKE'innodb_buffer_pool%';freelinkedlistMySQL服务器刚启动的时候,此时缓冲池中还没有缓存真正的磁盘页面,随着程序的运行,会不断有磁盘上的页面缓存在缓冲池中。从磁盘读取一个页面到缓冲池中应该放在哪个缓存页面?思路:区分缓冲池中哪些缓存页是空闲的,哪些已经被使用了。将所有空闲缓存页对应的控制块作为节点放入一个链表中,称为空闲链表。Flushlinkedlist如果我们修改缓冲池中某个缓存页的数据,就会和磁盘上的页不一致。这样的缓存页也称为脏页(dirtypage)。最简单的方法是每次发生修改时立即同步到磁盘上的相应页面,但是频繁向磁盘写入数据会严重影响程序的性能。因此,Innodb创建了一个链表来存放脏页,所有被修改的缓存页对应的控制块都会作为一个节点加入到一个链表中,并在以后的某个时间点进行同步。该链表称为刷新链表。Insufficientcache缓冲池对应的内存大小毕竟是有限的。如果要缓存的页面占用的内存大小超过缓冲池的大小,即没有多余的空闲缓存页面时怎么办?从缓冲池中删除一些旧的缓存页面,然后放回新的缓存页面。删除了哪些缓存页面?这就需要引入缓存淘汰机制。缓存淘汰机制缓存淘汰有以下两个目的:实现淘汰使缓存命中率高。最常用的缓存淘汰机制是LRU(Leastrecentlyused)算法。传统LRULRU的两种情况:(1)页面已经在缓冲池中,所以只执行“移动”到LRU头部的动作,没有页面被淘汰;(2)页面不在缓冲池中,除了“放入”LRU头的动作外,还有“移除”LRU尾页的动作;在InnoDB中,传统的LRU会遇到两种问题:(1)预读失败;(2)缓冲池污染;什么是预读无效?由于预读(Read-Ahead),页面被提前放入缓冲池,但是最后MySQL不从页面读取数据,称为预读失败。如何优化预读失效?优化预读失败,思路是:(1)让预读失败的页面留在缓冲区中poolLRUforasasasshorttimeaspossibletime;(2)让真正被读取的pages移动到bufferpoolLRU的头部,保证真正被读取的热数据在bufferpool中停留的时间尽可能长.具体做法是:(1)将LRU分成两部分:新区(newsublist)和老区(oldsublist)(2)两个区首尾相连,即:新区的尾部连接到旧区的尾部头部(head);(3)当一个新的页(如预读页)加入缓冲池时,只是加入到老区的头部:如果数据真正被读取(预读成功),则被加入到新区中,如果header中的数据还没有被读取,那么会比新区中的“热点数据页”更早从缓冲池中淘汰。改进后的缓冲池LRU可以解决“无法预读”的问题。查看系统变量innodb_old_blocks_pct的值,判断old区在LRU列表中的占比SHOWVARIABLESLIKE'innodb_old_blocks_pct';什么是MySQL缓冲池污染?当某条SQL语句批量扫描大量数据时,可能会导致缓冲池中的所有页被替换,导致大量热数据被换出,MySQL性能急剧下降。这种情况称为缓冲池污染。比如有一个user表,数据量很大,在执行时select*fromuserwherenamelike"%test%";优化缓冲池污染,思路是:(1)防止批量扫描的大量数据进入新区;(2)将实际读取的页移到缓冲池LRU的头部;具体实现:增加“老区停留时间”机制:当老区的缓存页第一次被访问时,在其对应的控制块中记录访问时间,如果后续再次访问时间和第一次访问时间是在一定的时间间隔内(即老区的缓存页面存在时间在一定的时间间隔内),则页面不会从老区移动到新区的头部。上面的全表扫描是这样进行的:(1)扫描过程中,新插入的数据页被放入old区(2)一个数据页会存在多条记录,所以一个数据页会被访问多次(3)由于是顺序扫描,数据页第一次访问和最后一次访问的时间间隔不会超过1S,所以还是会停留在老区(4)继续扫描,之前的数据页永远不会再次被访问,所以不会被移动到新的区域,最终很快就会被淘汰。此间隔由系统变量innodb_old_blocks_time控制。显示像“innodb_old_blocks_time”这样的变量;配置缓冲池innodb_buffer_pool_sizeinnodb_buffer_pool_chunk_size×innodb_buffer_pool_instances时的注意事项(这主要是为了保证每个缓冲池实例包含相同数量的chunk)。查看BufferPool状态信息SHOWENGINEINNODBSTATUS\G部分参数如下:Totalmemoryallocated:表示BufferPool申请给操作系统的连续内存空间的大小,包括所有控制块、缓存页和碎片的大小.Bufferpoolsize:表示BufferPool可以容纳多少个缓存页,单位为pageFreebuffers:表示当前BufferPool还剩多少空闲缓存页,即空闲链表还剩多少节点.数据库页数:表示LRU链表的页数,包括新旧区的节点数。olddatabasepages:表示LRU链表old区的节点数。Modifieddbpages:表示脏页数,即flushlist中的节点数。总结1.磁盘太慢,需要用内存做缓存。2.缓冲池本质上是InnoDB申请给操作系统的一块连续的内存空间,其大小可以通过innodb_buffer_pool_size进行调整。3.InnoDB使用很多链表来管理缓冲池。4.缓冲池常用的管理算法是LRU5。InnoDB对普通的LRU进行了优化:分为新区和旧区,并增加了“停留时间”机制。更多好文,公众号持续更新中
