The innoDB buffer pool

The InnoDB Buffer Pool

InnoDB维持了一个存储空间叫buffer pool,在内存里缓存数据和索引。知道InnoDB的buffer pool如何工作,并且经常利用它访问内存中的数据是一个很重要的mysql访问调优方式。

你可以配置各种各样的InnoDB buffer pool方式来提升性能。

InnoDB Buffer Pool LRU 算法

InnoDB管理一个buffer pool列表,用一个最少最近使用(LRU)算法的变体。当房间需要增加一个新页面进pool时,InnoDB会清除最少最近用的页面,增加新页面到列表中间。这个“中点插入策略”按照以下两个子列表对待列表:

  • 在头部,一个新或者young页面子列表是最近访问的
  • 在尾部,一个旧页面子列表是最少最近访问的

这个算法使得在新子列表中页面能被大量查询到。旧的子列表包含最少使用页面;这些页面是清除的候选。

LRU算法默认按照以下几点操作:

  • 3/8的buffer pool专门给旧的子列表用
  • 列表中点是新子列表尾部遇到旧子列表头部的边界
  • 当InnoDB读取一个页面进buffer pool时,起初会被插入到中点(旧子列表的头部)。这个页面能被读取是因为它包含在用户的特殊操作中,比如一个SQL查询,或者是在InnoDB的自动预读取操作部分。
  • 随着数据库的操作,在buffer pool中未被访问的页面通过向列表尾部移动而“老化”。新旧子列表中的页面随着其他页面变新而变旧。旧子列表中页面也随着页面被插入到中点而变旧。最终,很长时间没被使用的页面到达旧列表尾部而被清除。

默认,被查询的页面会立即移动到新子列表,意味着它们将会在很长一段时间内留在buffer pool。一个表扫描(例如一个mysqldump操作,或者没有where的select语句)会带很大的数据进入buffer poll,然后清除大量的旧数据,即使这个新数据从来不会被用。类似的,后台进程预读加载的页面,只有被移到新列表的头部才会被访问到。这些情况会经常推送有用的页面到旧子列表,然后它们成为清除的对象。关于这些行为的更多信息,查看Section 15.6.3.4, “Making the Buffer Pool Scan Resistant”Section 15.6.3.5, “Configuring InnoDB Buffer Pool Prefetching (Read-Ahead)”.

InnoDB标准监控输出包含buffer pool和内存部分的几个领域,这些从属于buffer pool LRU算法。更多细节查看Section 15.6.3.9, “Monitoring the Buffer Pool Using the InnoDB Standard Monitor”.

InnoDB Buffer Pool 配置选项

几个影响InnoDB buffer pool不同部分的配置选项。

设置innodb_old_blocks_time值超过0可以防止一次表扫描扫大量的新子列表,只为了这次扫描。一次页面扫描读取行被快速连续访问多次,但是之后这个页面却是没用的。如果innodb_old_blocks_time设置的值比处理页面的时间大,那么在旧子列表的和该到列表尾部的页面将被很快的清除。这样,只被一次表扫描用的页面不会对新子列表中的大量使用的页面造成损害。

innodb_old_blocks_time可以在运行时设置,所以你可以在执行诸如表扫描和下载操作的时候暂时改变它:

SET GLOBAL innodb_old_blocks_time = 1000;
... perform queries that scan tables ...
SET GLOBAL innodb_old_blocks_time = 0;

这个策略并不会执行,如果你的意图是想通过充满一个表的内容来热启动buffer pool。比如,基准测试经常在服务启动时做一个表或者索引扫描,因为当用了一段时间之后,数据通常都在buffer pool里面。在这种情况,把innodb_old_blocks_time设为0,至少等到热启动完成。查看Section 15.6.3.4, “Making the Buffer Pool Scan Resistant”.