Buffer Pool是主内存中用来缓存被访问的数据和索引的区域,它允许经常被访问的数据直接在内存中执行,来提高执行速度。在专用的服务器上,建议分配80%的物理内存给buffer pool.
为了提高高容量读操作,buffer pool被划分成了包含多行数据的页(page),为了缓存管理的效率,buffer pool被实现为一个页的链表;使用LRU算法来淘汰最近很少被使用的数据。
了解如何利用buffer pool将频繁被访问的数据保存在内存中是Mysql优化的一个重要方面。
Buffer Pool LRU 算法
Buffer pool作为一个链表来管理,使用的是最近最少使用算法(LRU)的一个变体。当需要增加一个新页到buffer pool中时,最近最少被使用的页会被驱逐,一个新的页会添加到链表的中间(middle)。这种中间点插入策略将这个链表当着两个子链:
- 在顶部,是最近访问的新页(new page)子链
- 在底部,是较少访问的旧页(old page)子链
这个算法保持被重度使用的数据在新页子链(new subList)。old subList包含了较少被使用的页,同时这些页也是等待被驱逐的页。默认情况如下:
- 3/8的buffer pool分配给old sublist
- 插入中间点(midpoint)是new sublist的尾部,同时页是old sublist的顶部。
- 当InnoDB读一个页到buffer pool时,它被插入到这个中间点(old sublist的头部),一个页被读取可能是因为用户指定的操作比如SQL查询,或者InnoDB自动执行的预读(read-ahead)操作。
- 访问一个在old sublist中的页会使它变成年轻(yong),移动这一页到buffer pool的头部(new sublist的头部)。如果因为需要而读取页,则立刻会进行第一次访问,这一页会变成yong。如果页是由于预读而读取页,则第一次访问不会马上发生(也可能在页被驱逐之前都不会发生)。
- 随着数据库的允许,buffer pool中未被访问的页会朝着链表的尾部移动而老化。新旧子链中的页都会随着其他页变成新页而老化。在old sublist中的页还会随着中间点插入的页而老化。最终,一直未被使用的页到达old sublist的尾部并被驱逐。
默认情况,查询读取的页立刻进入new sublist,意味着它们停留在buffer pool中的时间更长。全部扫描(比如执行mysqldump操作,或者一个没有条件的查询语句)会将大量数据带入buffer pool,并驱逐等量的旧数据,即使这些数据以后不会被使用。类似的,后台预读线程加载的页即使被访问一次就到了buffer pool链表的头部。这些都会将经常使用的页推到old sublist中,然后成为被驱逐的对象,优化这种行为通过下面的参数
innodb_old_blocks_pct
old sublist在整个buffer pool 的百分比,默认为 37,可以取值的范围为 5-95.innodb_old_blocks_time 数据在old sublist中的时间窗口,指定了页从第一次访问后多长时间内不会移动这一页到new sublist
Buffer Pool的配置参数:
innodb_buffer_pool_size 配置buffer pool的总大小;
innodb_buffer_pool_chunk_size
配置每个buffer pool 实例的大小,默认值为 128M;innodb_buffer_pool_instances 配置实例个数;
innodb_buffer_pool_size 应该等于或者是 innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances 的整数倍,比如我们配置 innodb_buffer_pool_size为8G,innodb_buffer_pool_instances=16,innodb_buffer_pool_chunk_size 不显示配置,默认为128M,这时 innodb_buffer_pool_chunk_size * innodb_buffer_pool_instances= 2G,8G是2G的整数倍,所以innodb_buffer_pool_size=8G是一个有效的值;如果配置innodb_buffer_pool_size=9G,这时候9G不是2G的整数倍,innodb会自动调整innodb_buffer_pool_size的值到10G或者8G。
(翻译自MySQL官方文档)